style(ui): 统一页面字号间距与状态样式
This commit is contained in:
1 parent
df5fbe5b08
commit
c5bfd006fa
23 files changed
+542
-569
No files matched your search
@@ -10,6 +10,7 @@ abstract final class AppDimens {
|
|||||||
|
|
||||||
static const double pageHorizontal = 16;
|
static const double pageHorizontal = 16;
|
||||||
static const double cardRadius = 16;
|
static const double cardRadius = 16;
|
||||||
|
static const double authCardRadius = 20;
|
||||||
static const double cardPadding = 16;
|
static const double cardPadding = 16;
|
||||||
static const double innerRadius = 12;
|
static const double innerRadius = 12;
|
||||||
static const double innerPadding = 12;
|
static const double innerPadding = 12;
|
||||||
|
|||||||
@@ -30,15 +30,18 @@ class AppBottomNav extends StatelessWidget {
|
|||||||
super.key,
|
super.key,
|
||||||
required this.items,
|
required this.items,
|
||||||
this.backgroundColor = AppColors.bar,
|
this.backgroundColor = AppColors.bar,
|
||||||
|
this.barKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
final List<AppBottomNavItem> items;
|
final List<AppBottomNavItem> items;
|
||||||
final Color backgroundColor;
|
final Color backgroundColor;
|
||||||
|
final Key? barKey;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => SafeArea(
|
Widget build(BuildContext context) => SafeArea(
|
||||||
top: false,
|
top: false,
|
||||||
child: Container(
|
child: Container(
|
||||||
|
key: barKey,
|
||||||
height: AppDimens.navHeight,
|
height: AppDimens.navHeight,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: backgroundColor,
|
color: backgroundColor,
|
||||||
@@ -81,7 +84,7 @@ class _NavDestination extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: item.selected ? AppColors.blue : Colors.transparent,
|
color: item.selected ? AppColors.blue : Colors.transparent,
|
||||||
borderRadius: BorderRadius.circular(14),
|
borderRadius: BorderRadius.circular(AppDimens.pillRadius),
|
||||||
),
|
),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
item.selected ? item.selectedIcon : item.icon,
|
item.selected ? item.selectedIcon : item.icon,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../theme/app_colors.dart';
|
import '../theme/app_colors.dart';
|
||||||
|
import '../theme/app_dimens.dart';
|
||||||
|
|
||||||
const double _regularIconSize = 48;
|
const double _regularIconSize = 48;
|
||||||
const double _compactIconSize = 32;
|
const double _compactIconSize = 32;
|
||||||
@@ -139,7 +140,7 @@ class _EmptyCopy extends StatelessWidget {
|
|||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: AppColors.muted,
|
color: AppColors.muted,
|
||||||
fontSize: 12.5,
|
fontSize: AppDimens.caption,
|
||||||
height: 1.45,
|
height: 1.45,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -151,11 +152,14 @@ class _EmptyCopy extends StatelessWidget {
|
|||||||
key: actionKey ?? const Key('empty-state-action'),
|
key: actionKey ?? const Key('empty-state-action'),
|
||||||
onPressed: onAction,
|
onPressed: onAction,
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
foregroundColor: scheme.primary,
|
foregroundColor: AppColors.blueSoft,
|
||||||
minimumSize: const Size(44, _actionMinHeight),
|
minimumSize: const Size(44, _actionMinHeight),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 14),
|
padding: const EdgeInsets.symmetric(horizontal: 14),
|
||||||
),
|
),
|
||||||
child: Text(actionLabel!, style: const TextStyle(fontSize: 13)),
|
child: Text(
|
||||||
|
actionLabel!,
|
||||||
|
style: const TextStyle(fontSize: AppDimens.body),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
import '../data/models/drama.dart';
|
import '../data/models/drama.dart';
|
||||||
import '../theme/app_colors.dart';
|
import '../theme/app_colors.dart';
|
||||||
|
import '../theme/app_dimens.dart';
|
||||||
import 'remote_image.dart';
|
import 'remote_image.dart';
|
||||||
|
|
||||||
/// 收藏等账号列表复用的剧集卡片,视觉与"我的"页列表保持一致。
|
/// 收藏等账号列表复用的剧集卡片,视觉与"我的"页列表保持一致。
|
||||||
@@ -23,16 +24,16 @@ class LibraryDramaTile extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Material(
|
return Material(
|
||||||
color: AppColors.card,
|
color: AppColors.card,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(AppDimens.innerRadius),
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(9),
|
padding: const EdgeInsets.all(AppDimens.innerPadding),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(AppDimens.innerRadius),
|
||||||
child: RemoteImage(drama.posterUrl, width: 74, height: 103),
|
child: RemoteImage(drama.posterUrl, width: 74, height: 103),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 11),
|
const SizedBox(width: 11),
|
||||||
@@ -46,7 +47,7 @@ class LibraryDramaTile extends StatelessWidget {
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
fontSize: 15,
|
fontSize: AppDimens.listTitle,
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -57,7 +58,7 @@ class LibraryDramaTile extends StatelessWidget {
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: AppColors.muted,
|
color: AppColors.muted,
|
||||||
fontSize: 12,
|
fontSize: AppDimens.caption,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import '../../core/data/repositories/account_repository.dart';
|
|||||||
import '../../core/data/services/auth_controller.dart';
|
import '../../core/data/services/auth_controller.dart';
|
||||||
import '../../core/formatting/localized_date_time.dart';
|
import '../../core/formatting/localized_date_time.dart';
|
||||||
import '../../core/theme/app_colors.dart';
|
import '../../core/theme/app_colors.dart';
|
||||||
|
import '../../core/theme/app_dimens.dart';
|
||||||
import '../../core/theme/app_theme.dart';
|
import '../../core/theme/app_theme.dart';
|
||||||
import '../../core/widgets/auth_gate.dart';
|
import '../../core/widgets/auth_gate.dart';
|
||||||
import '../../l10n/app_localizations.dart';
|
import '../../l10n/app_localizations.dart';
|
||||||
@@ -58,12 +59,15 @@ class _AccountDeactivatePageState extends State<AccountDeactivatePage> {
|
|||||||
foregroundColor: AppColors.onPrimary,
|
foregroundColor: AppColors.onPrimary,
|
||||||
title: Text(l10n.accountDeactivate),
|
title: Text(l10n.accountDeactivate),
|
||||||
),
|
),
|
||||||
body: canShow
|
body: SafeArea(
|
||||||
? ListenableBuilder(
|
top: false,
|
||||||
listenable: _logic,
|
child: canShow
|
||||||
builder: (context, _) => _body(l10n),
|
? ListenableBuilder(
|
||||||
)
|
listenable: _logic,
|
||||||
: const AuthGatePanel(),
|
builder: (context, _) => _body(l10n),
|
||||||
|
)
|
||||||
|
: const AuthGatePanel(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -90,7 +94,7 @@ class _AccountDeactivatePageState extends State<AccountDeactivatePage> {
|
|||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: errorColor.withValues(alpha: 0.10),
|
color: errorColor.withValues(alpha: 0.10),
|
||||||
borderRadius: BorderRadius.circular(14),
|
borderRadius: BorderRadius.circular(AppDimens.cardRadius),
|
||||||
border: Border.all(color: errorColor.withValues(alpha: 0.35)),
|
border: Border.all(color: errorColor.withValues(alpha: 0.35)),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
@@ -105,7 +109,7 @@ class _AccountDeactivatePageState extends State<AccountDeactivatePage> {
|
|||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.card,
|
color: AppColors.card,
|
||||||
borderRadius: BorderRadius.circular(14),
|
borderRadius: BorderRadius.circular(AppDimens.cardRadius),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
l10n.accountScheduledFor(
|
l10n.accountScheduledFor(
|
||||||
@@ -161,7 +165,10 @@ class _AccountDeactivatePageState extends State<AccountDeactivatePage> {
|
|||||||
|
|
||||||
Widget _errorText(String value) => Text(
|
Widget _errorText(String value) => Text(
|
||||||
value,
|
value,
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.error, fontSize: 12),
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.error,
|
||||||
|
fontSize: AppDimens.caption,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
Future<void> _confirm() async {
|
Future<void> _confirm() async {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import '../../core/data/services/auth_controller.dart';
|
|||||||
import '../../core/formatting/error_text.dart';
|
import '../../core/formatting/error_text.dart';
|
||||||
import '../../core/formatting/localized_date_time.dart';
|
import '../../core/formatting/localized_date_time.dart';
|
||||||
import '../../core/theme/app_colors.dart';
|
import '../../core/theme/app_colors.dart';
|
||||||
|
import '../../core/theme/app_dimens.dart';
|
||||||
import '../../core/theme/app_theme.dart';
|
import '../../core/theme/app_theme.dart';
|
||||||
import '../../core/widgets/auth_gate.dart';
|
import '../../core/widgets/auth_gate.dart';
|
||||||
import '../../core/widgets/empty_state.dart';
|
import '../../core/widgets/empty_state.dart';
|
||||||
@@ -58,12 +59,15 @@ class _AccountDevicesPageState extends State<AccountDevicesPage> {
|
|||||||
foregroundColor: AppColors.onPrimary,
|
foregroundColor: AppColors.onPrimary,
|
||||||
title: Text(l10n.accountDevices),
|
title: Text(l10n.accountDevices),
|
||||||
),
|
),
|
||||||
body: signedIn
|
body: SafeArea(
|
||||||
? ListenableBuilder(
|
top: false,
|
||||||
listenable: _logic,
|
child: signedIn
|
||||||
builder: (context, _) => _body(l10n),
|
? ListenableBuilder(
|
||||||
)
|
listenable: _logic,
|
||||||
: const AuthGatePanel(),
|
builder: (context, _) => _body(l10n),
|
||||||
|
)
|
||||||
|
: const AuthGatePanel(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -109,10 +113,10 @@ class _DeviceTile extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
key: ValueKey('account-device-${device.id}'),
|
key: ValueKey('account-device-${device.id}'),
|
||||||
padding: const EdgeInsets.all(14),
|
padding: const EdgeInsets.all(AppDimens.cardPadding),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.card,
|
color: AppColors.card,
|
||||||
borderRadius: BorderRadius.circular(14),
|
borderRadius: BorderRadius.circular(AppDimens.cardRadius),
|
||||||
border: Border.all(color: AppColors.outlineVariantDark),
|
border: Border.all(color: AppColors.outlineVariantDark),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -131,7 +135,7 @@ class _DeviceTile extends StatelessWidget {
|
|||||||
? device.platform
|
? device.platform
|
||||||
: device.deviceModel,
|
: device.deviceModel,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 15,
|
fontSize: AppDimens.listTitle,
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -164,14 +168,17 @@ class _DeviceTile extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
label,
|
label,
|
||||||
style: const TextStyle(color: AppColors.muted, fontSize: 12),
|
style: const TextStyle(
|
||||||
|
color: AppColors.muted,
|
||||||
|
fontSize: AppDimens.caption,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Flexible(
|
Flexible(
|
||||||
child: Text(
|
child: Text(
|
||||||
value.isEmpty ? '-' : value,
|
value.isEmpty ? '-' : value,
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
style: const TextStyle(fontSize: 12),
|
style: const TextStyle(fontSize: AppDimens.caption),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -188,20 +195,19 @@ class _StatusChip extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final active = status.toLowerCase() == 'active';
|
final active = status.toLowerCase() == 'active';
|
||||||
final scheme = Theme.of(context).colorScheme;
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: active
|
color: active
|
||||||
? scheme.tertiary.withValues(alpha: 0.16)
|
? AppColors.gold.withValues(alpha: 0.16)
|
||||||
: AppColors.raised,
|
: AppColors.raised,
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(AppDimens.pillRadius),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
active ? l10n.accountDeviceActive : l10n.accountDeviceInactive,
|
active ? l10n.accountDeviceActive : l10n.accountDeviceInactive,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: active ? scheme.tertiary : AppColors.muted,
|
color: active ? AppColors.gold : AppColors.muted,
|
||||||
fontSize: 11,
|
fontSize: AppDimens.caption,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../../core/theme/app_colors.dart';
|
import '../../core/theme/app_colors.dart';
|
||||||
|
import '../../core/theme/app_dimens.dart';
|
||||||
import '../../l10n/app_localizations.dart';
|
import '../../l10n/app_localizations.dart';
|
||||||
import 'account_deactivate_page.dart';
|
import 'account_deactivate_page.dart';
|
||||||
import 'account_devices_page.dart';
|
import 'account_devices_page.dart';
|
||||||
import 'account_export_page.dart';
|
import 'account_export_page.dart';
|
||||||
import 'account_profile_page.dart';
|
import 'account_profile_page.dart';
|
||||||
|
|
||||||
const _accountCardHeight = 48.0;
|
|
||||||
const _accountTitleWidth = 64.0;
|
const _accountTitleWidth = 64.0;
|
||||||
|
|
||||||
/// 我的页账号入口卡;只在登录态由父页面展示。
|
/// 我的页账号入口卡;只在登录态由父页面展示。
|
||||||
@@ -25,7 +25,7 @@ class AccountEntryCard extends StatelessWidget {
|
|||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: _accountCardHeight,
|
height: AppDimens.accountCardHeight,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
@@ -35,7 +35,7 @@ class AccountEntryCard extends StatelessWidget {
|
|||||||
child: Text(
|
child: Text(
|
||||||
l10n.accountCenter,
|
l10n.accountCenter,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 12,
|
fontSize: AppDimens.caption,
|
||||||
fontWeight: FontWeight.w800,
|
fontWeight: FontWeight.w800,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -109,14 +109,21 @@ class AccountEntryCard extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Icon(icon, color: AppColors.blueSoft, size: 16),
|
Icon(
|
||||||
|
icon,
|
||||||
|
color: AppColors.blueSoft,
|
||||||
|
size: AppDimens.accountIcon,
|
||||||
|
),
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 2),
|
||||||
Text(
|
Text(
|
||||||
title,
|
title,
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(fontSize: 9.5, height: 1.05),
|
style: const TextStyle(
|
||||||
|
fontSize: AppDimens.accountLabel,
|
||||||
|
height: 1.15,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import '../../core/data/repositories/account_repository.dart';
|
|||||||
import '../../core/data/services/auth_controller.dart';
|
import '../../core/data/services/auth_controller.dart';
|
||||||
import '../../core/formatting/error_text.dart';
|
import '../../core/formatting/error_text.dart';
|
||||||
import '../../core/theme/app_colors.dart';
|
import '../../core/theme/app_colors.dart';
|
||||||
|
import '../../core/theme/app_dimens.dart';
|
||||||
import '../../core/theme/app_theme.dart';
|
import '../../core/theme/app_theme.dart';
|
||||||
import '../../core/widgets/auth_gate.dart';
|
import '../../core/widgets/auth_gate.dart';
|
||||||
import '../../core/widgets/empty_state.dart';
|
import '../../core/widgets/empty_state.dart';
|
||||||
@@ -59,12 +60,15 @@ class _AccountExportPageState extends State<AccountExportPage> {
|
|||||||
foregroundColor: AppColors.onPrimary,
|
foregroundColor: AppColors.onPrimary,
|
||||||
title: Text(l10n.accountDataExport),
|
title: Text(l10n.accountDataExport),
|
||||||
),
|
),
|
||||||
body: signedIn
|
body: SafeArea(
|
||||||
? ListenableBuilder(
|
top: false,
|
||||||
listenable: _logic,
|
child: signedIn
|
||||||
builder: (context, _) => _body(l10n),
|
? ListenableBuilder(
|
||||||
)
|
listenable: _logic,
|
||||||
: const AuthGatePanel(),
|
builder: (context, _) => _body(l10n),
|
||||||
|
)
|
||||||
|
: const AuthGatePanel(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -106,7 +110,10 @@ class _AccountExportPageState extends State<AccountExportPage> {
|
|||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
'${l10n.accountUid}: ${data.profile.uid}',
|
'${l10n.accountUid}: ${data.profile.uid}',
|
||||||
style: const TextStyle(color: AppColors.muted, fontSize: 12),
|
style: const TextStyle(
|
||||||
|
color: AppColors.muted,
|
||||||
|
fontSize: AppDimens.caption,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -115,7 +122,10 @@ class _AccountExportPageState extends State<AccountExportPage> {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
l10n.accountCredentials,
|
l10n.accountCredentials,
|
||||||
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w700),
|
style: const TextStyle(
|
||||||
|
fontSize: AppDimens.listTitle,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
for (final credential in data.credentials)
|
for (final credential in data.credentials)
|
||||||
ListTile(
|
ListTile(
|
||||||
@@ -128,7 +138,10 @@ class _AccountExportPageState extends State<AccountExportPage> {
|
|||||||
subtitle: Text(credential.type),
|
subtitle: Text(credential.type),
|
||||||
trailing: Text(
|
trailing: Text(
|
||||||
credential.verified ? l10n.accountVerified : l10n.accountUnverified,
|
credential.verified ? l10n.accountVerified : l10n.accountUnverified,
|
||||||
style: const TextStyle(color: AppColors.muted, fontSize: 11),
|
style: const TextStyle(
|
||||||
|
color: AppColors.muted,
|
||||||
|
fontSize: AppDimens.caption,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_summary(l10n.accountConsentRecords, '${data.consentCount}'),
|
_summary(l10n.accountConsentRecords, '${data.consentCount}'),
|
||||||
@@ -144,10 +157,10 @@ class _AccountExportPageState extends State<AccountExportPage> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Widget _card({required List<Widget> children}) => Container(
|
Widget _card({required List<Widget> children}) => Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(AppDimens.cardPadding),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.card,
|
color: AppColors.card,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(AppDimens.cardRadius),
|
||||||
border: Border.all(color: AppColors.outlineVariantDark),
|
border: Border.all(color: AppColors.outlineVariantDark),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -163,7 +176,10 @@ class _AccountExportPageState extends State<AccountExportPage> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
label,
|
label,
|
||||||
style: const TextStyle(color: AppColors.muted, fontSize: 12),
|
style: const TextStyle(
|
||||||
|
color: AppColors.muted,
|
||||||
|
fontSize: AppDimens.caption,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(value, style: const TextStyle(fontWeight: FontWeight.w700)),
|
Text(value, style: const TextStyle(fontWeight: FontWeight.w700)),
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import '../../core/data/services/auth_controller.dart';
|
|||||||
import '../../core/formatting/error_text.dart';
|
import '../../core/formatting/error_text.dart';
|
||||||
import '../../core/formatting/localized_date_time.dart';
|
import '../../core/formatting/localized_date_time.dart';
|
||||||
import '../../core/theme/app_colors.dart';
|
import '../../core/theme/app_colors.dart';
|
||||||
|
import '../../core/theme/app_dimens.dart';
|
||||||
import '../../core/theme/app_theme.dart';
|
import '../../core/theme/app_theme.dart';
|
||||||
import '../../core/widgets/auth_gate.dart';
|
import '../../core/widgets/auth_gate.dart';
|
||||||
import '../../core/widgets/empty_state.dart';
|
import '../../core/widgets/empty_state.dart';
|
||||||
@@ -79,12 +80,15 @@ class _AccountProfilePageState extends State<AccountProfilePage> {
|
|||||||
foregroundColor: AppColors.onPrimary,
|
foregroundColor: AppColors.onPrimary,
|
||||||
title: Text(l10n.accountProfile),
|
title: Text(l10n.accountProfile),
|
||||||
),
|
),
|
||||||
body: signedIn
|
body: SafeArea(
|
||||||
? ListenableBuilder(
|
top: false,
|
||||||
listenable: _logic,
|
child: signedIn
|
||||||
builder: (context, _) => _body(l10n),
|
? ListenableBuilder(
|
||||||
)
|
listenable: _logic,
|
||||||
: const AuthGatePanel(),
|
builder: (context, _) => _body(l10n),
|
||||||
|
)
|
||||||
|
: const AuthGatePanel(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -139,14 +143,17 @@ class _AccountProfilePageState extends State<AccountProfilePage> {
|
|||||||
Text(
|
Text(
|
||||||
profile.nickname,
|
profile.nickname,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 20,
|
fontSize: AppDimens.pageTitle,
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
'${l10n.accountUid}: ${profile.uid}',
|
'${l10n.accountUid}: ${profile.uid}',
|
||||||
style: const TextStyle(color: AppColors.muted, fontSize: 12),
|
style: const TextStyle(
|
||||||
|
color: AppColors.muted,
|
||||||
|
fontSize: AppDimens.caption,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -211,7 +218,10 @@ class _AccountProfilePageState extends State<AccountProfilePage> {
|
|||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
Text(
|
Text(
|
||||||
l10n.accountBirthdayHint,
|
l10n.accountBirthdayHint,
|
||||||
style: const TextStyle(color: AppColors.muted, fontSize: 12),
|
style: const TextStyle(
|
||||||
|
color: AppColors.muted,
|
||||||
|
fontSize: AppDimens.caption,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -222,7 +232,7 @@ class _AccountProfilePageState extends State<AccountProfilePage> {
|
|||||||
errorText(l10n, _logic.error, l10n.accountProfileSaveFailed),
|
errorText(l10n, _logic.error, l10n.accountProfileSaveFailed),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).colorScheme.error,
|
color: Theme.of(context).colorScheme.error,
|
||||||
fontSize: 12,
|
fontSize: AppDimens.caption,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -250,7 +260,10 @@ class _AccountProfilePageState extends State<AccountProfilePage> {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
l10n.accountReadOnlyInfo,
|
l10n.accountReadOnlyInfo,
|
||||||
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w700),
|
style: const TextStyle(
|
||||||
|
fontSize: AppDimens.listTitle,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
_info(l10n.accountMaskedAccount, profile.maskedTarget),
|
_info(l10n.accountMaskedAccount, profile.maskedTarget),
|
||||||
@@ -283,14 +296,17 @@ class _AccountProfilePageState extends State<AccountProfilePage> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
label,
|
label,
|
||||||
style: const TextStyle(color: AppColors.muted, fontSize: 12),
|
style: const TextStyle(
|
||||||
|
color: AppColors.muted,
|
||||||
|
fontSize: AppDimens.caption,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Flexible(
|
Flexible(
|
||||||
child: Text(
|
child: Text(
|
||||||
value.isEmpty ? '-' : value,
|
value.isEmpty ? '-' : value,
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
style: const TextStyle(fontSize: 13),
|
style: const TextStyle(fontSize: AppDimens.body),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -337,10 +353,10 @@ class _AccountCard extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => Container(
|
Widget build(BuildContext context) => Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(AppDimens.cardPadding),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.card,
|
color: AppColors.card,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(AppDimens.cardRadius),
|
||||||
border: Border.all(color: AppColors.outlineVariantDark),
|
border: Border.all(color: AppColors.outlineVariantDark),
|
||||||
),
|
),
|
||||||
child: child,
|
child: child,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import '../../core/data/remote/api_client.dart';
|
|||||||
import '../../core/data/services/auth_controller.dart';
|
import '../../core/data/services/auth_controller.dart';
|
||||||
import '../../core/platform/haptics.dart';
|
import '../../core/platform/haptics.dart';
|
||||||
import '../../core/theme/app_colors.dart';
|
import '../../core/theme/app_colors.dart';
|
||||||
|
import '../../core/theme/app_dimens.dart';
|
||||||
import '../../core/theme/app_theme.dart';
|
import '../../core/theme/app_theme.dart';
|
||||||
import '../../l10n/app_localizations.dart';
|
import '../../l10n/app_localizations.dart';
|
||||||
import 'auth_logic.dart';
|
import 'auth_logic.dart';
|
||||||
@@ -19,8 +20,9 @@ part 'auth_form_widgets.dart';
|
|||||||
const double _minTapTarget = 44;
|
const double _minTapTarget = 44;
|
||||||
const double _brandMarkSize = 48;
|
const double _brandMarkSize = 48;
|
||||||
const double _contentMaxWidth = 440;
|
const double _contentMaxWidth = 440;
|
||||||
const double _cardRadius = 20;
|
// Auth keeps its approved 20px card silhouette.
|
||||||
const double _cardPadding = 20;
|
const double _cardRadius = AppDimens.authCardRadius;
|
||||||
|
const double _cardPadding = AppDimens.cardPadding;
|
||||||
const double _cardBorderWidth = 1;
|
const double _cardBorderWidth = 1;
|
||||||
const double _fieldHeight = 52;
|
const double _fieldHeight = 52;
|
||||||
const double _fieldRadius = 14;
|
const double _fieldRadius = 14;
|
||||||
@@ -163,9 +165,9 @@ class _AuthPageState extends State<AuthPage> {
|
|||||||
keyboardDismissBehavior:
|
keyboardDismissBehavior:
|
||||||
ScrollViewKeyboardDismissBehavior.onDrag,
|
ScrollViewKeyboardDismissBehavior.onDrag,
|
||||||
padding: EdgeInsets.fromLTRB(
|
padding: EdgeInsets.fromLTRB(
|
||||||
20,
|
AppDimens.pageHorizontal,
|
||||||
12,
|
12,
|
||||||
20,
|
AppDimens.pageHorizontal,
|
||||||
28 + MediaQuery.viewInsetsOf(context).bottom,
|
28 + MediaQuery.viewInsetsOf(context).bottom,
|
||||||
),
|
),
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ class _AuthCard extends StatelessWidget {
|
|||||||
l10n.authSubtitle,
|
l10n.authSubtitle,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: AppColors.muted,
|
color: AppColors.muted,
|
||||||
fontSize: 13,
|
fontSize: AppDimens.body,
|
||||||
height: 1.45,
|
height: 1.45,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ import '../../core/data/repositories/watch_history_repository.dart';
|
|||||||
import '../../core/data/services/catalog_controller.dart';
|
import '../../core/data/services/catalog_controller.dart';
|
||||||
import '../../core/data/services/category_controller.dart';
|
import '../../core/data/services/category_controller.dart';
|
||||||
import '../../core/theme/app_colors.dart';
|
import '../../core/theme/app_colors.dart';
|
||||||
|
import '../../core/theme/app_dimens.dart';
|
||||||
import '../../core/theme/app_theme.dart';
|
import '../../core/theme/app_theme.dart';
|
||||||
|
import '../../core/widgets/app_bottom_nav.dart';
|
||||||
import '../../core/widgets/empty_state.dart';
|
import '../../core/widgets/empty_state.dart';
|
||||||
import '../../core/widgets/remote_image.dart';
|
import '../../core/widgets/remote_image.dart';
|
||||||
import '../../l10n/app_localizations.dart';
|
import '../../l10n/app_localizations.dart';
|
||||||
@@ -261,7 +263,7 @@ class _DiscoverPageState extends State<DiscoverPage> {
|
|||||||
];
|
];
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
key: const Key('home-category-chips'),
|
key: const Key('home-category-chips'),
|
||||||
height: 36,
|
height: AppDimens.tapTarget,
|
||||||
child: ListView.separated(
|
child: ListView.separated(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
itemCount: choices.length,
|
itemCount: choices.length,
|
||||||
@@ -269,24 +271,31 @@ class _DiscoverPageState extends State<DiscoverPage> {
|
|||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final choice = choices[index];
|
final choice = choices[index];
|
||||||
final selected = categories.selectedCode == choice.code;
|
final selected = categories.selectedCode == choice.code;
|
||||||
return GestureDetector(
|
return Semantics(
|
||||||
onTap: () => categories.selectCode(choice.code),
|
selected: selected,
|
||||||
child: AnimatedContainer(
|
button: true,
|
||||||
duration: const Duration(milliseconds: 180),
|
child: GestureDetector(
|
||||||
alignment: Alignment.center,
|
behavior: HitTestBehavior.opaque,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 14),
|
onTap: () => categories.selectCode(choice.code),
|
||||||
decoration: BoxDecoration(
|
child: Center(
|
||||||
color: selected ? AppColors.blue : AppColors.raised,
|
child: AnimatedContainer(
|
||||||
borderRadius: BorderRadius.circular(18),
|
duration: const Duration(milliseconds: 180),
|
||||||
),
|
alignment: Alignment.center,
|
||||||
child: Text(
|
padding: const EdgeInsets.symmetric(horizontal: 14),
|
||||||
choice.label,
|
decoration: BoxDecoration(
|
||||||
maxLines: 1,
|
color: selected ? AppColors.blue : AppColors.raised,
|
||||||
style: TextStyle(
|
borderRadius: BorderRadius.circular(AppDimens.pillRadius),
|
||||||
color: selected ? AppColors.onPrimary : AppColors.cream,
|
),
|
||||||
fontSize: 13,
|
child: Text(
|
||||||
fontWeight: selected ? FontWeight.w700 : FontWeight.w500,
|
choice.label,
|
||||||
height: 1.1,
|
maxLines: 1,
|
||||||
|
style: TextStyle(
|
||||||
|
color: selected ? AppColors.onPrimary : AppColors.cream,
|
||||||
|
fontSize: AppDimens.body,
|
||||||
|
fontWeight: selected ? FontWeight.w700 : FontWeight.w500,
|
||||||
|
height: 1.1,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -296,83 +305,41 @@ class _DiscoverPageState extends State<DiscoverPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _bottomNav(AppLocalizations l10n) => Container(
|
Widget _bottomNav(AppLocalizations l10n) => AppBottomNav(
|
||||||
height: 63,
|
items: [
|
||||||
decoration: const BoxDecoration(
|
AppBottomNavItem(
|
||||||
color: AppColors.bar,
|
tabKey: const Key('discover-nav-home'),
|
||||||
border: Border(top: BorderSide(color: AppColors.outlineVariantDark)),
|
icon: Icons.play_circle_outline_rounded,
|
||||||
),
|
selectedIcon: Icons.play_circle_fill_rounded,
|
||||||
child: Row(
|
label: l10n.homeTab,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
onTap: () => logic.act(
|
||||||
children: [
|
context,
|
||||||
_DiscoverNavItem(
|
DiscoverPageAction.home,
|
||||||
icon: Icons.play_circle_outline_rounded,
|
embedded: widget.embedded,
|
||||||
label: l10n.homeTab,
|
onAction: widget.onAction,
|
||||||
onTap: () => logic.act(
|
|
||||||
context,
|
|
||||||
DiscoverPageAction.home,
|
|
||||||
embedded: widget.embedded,
|
|
||||||
onAction: widget.onAction,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
_DiscoverNavItem(
|
|
||||||
icon: Icons.explore_outlined,
|
|
||||||
label: l10n.discoverTab,
|
|
||||||
selected: true,
|
|
||||||
onTap: () {},
|
|
||||||
),
|
|
||||||
_DiscoverNavItem(
|
|
||||||
icon: Icons.video_library_outlined,
|
|
||||||
label: l10n.myTab,
|
|
||||||
onTap: () => logic.act(
|
|
||||||
context,
|
|
||||||
DiscoverPageAction.my,
|
|
||||||
embedded: widget.embedded,
|
|
||||||
onAction: widget.onAction,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class _DiscoverNavItem extends StatelessWidget {
|
|
||||||
const _DiscoverNavItem({
|
|
||||||
required this.icon,
|
|
||||||
required this.label,
|
|
||||||
required this.onTap,
|
|
||||||
this.selected = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
final IconData icon;
|
|
||||||
final String label;
|
|
||||||
final VoidCallback onTap;
|
|
||||||
final bool selected;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) => InkWell(
|
|
||||||
onTap: onTap,
|
|
||||||
child: SizedBox(
|
|
||||||
width: 72,
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
icon,
|
|
||||||
size: 21,
|
|
||||||
color: selected ? AppColors.blueSoft : AppColors.muted,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
label,
|
|
||||||
style: TextStyle(
|
|
||||||
color: selected ? AppColors.blueSoft : AppColors.muted,
|
|
||||||
fontSize: 10,
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
AppBottomNavItem(
|
||||||
|
tabKey: const Key('discover-nav-current'),
|
||||||
|
icon: Icons.explore_outlined,
|
||||||
|
selectedIcon: Icons.explore_rounded,
|
||||||
|
label: l10n.discoverTab,
|
||||||
|
selected: true,
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
AppBottomNavItem(
|
||||||
|
tabKey: const Key('discover-nav-my'),
|
||||||
|
icon: Icons.video_library_outlined,
|
||||||
|
selectedIcon: Icons.video_library_rounded,
|
||||||
|
label: l10n.myTab,
|
||||||
|
onTap: () => logic.act(
|
||||||
|
context,
|
||||||
|
DiscoverPageAction.my,
|
||||||
|
embedded: widget.embedded,
|
||||||
|
onAction: widget.onAction,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -421,24 +388,27 @@ class _HeaderPill extends StatelessWidget {
|
|||||||
child: InkWell(
|
child: InkWell(
|
||||||
customBorder: const StadiumBorder(),
|
customBorder: const StadiumBorder(),
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: Padding(
|
child: ConstrainedBox(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 5),
|
constraints: const BoxConstraints(minHeight: AppDimens.tapTarget),
|
||||||
child: Row(
|
child: Padding(
|
||||||
mainAxisSize: MainAxisSize.min,
|
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||||
children: [
|
child: Row(
|
||||||
Icon(icon, size: 15, color: _coin),
|
mainAxisSize: MainAxisSize.min,
|
||||||
const SizedBox(width: 4),
|
children: [
|
||||||
Text(
|
Icon(icon, size: 15, color: _coin),
|
||||||
label,
|
const SizedBox(width: 4),
|
||||||
maxLines: 1,
|
Text(
|
||||||
style: const TextStyle(
|
label,
|
||||||
color: AppColors.cream,
|
maxLines: 1,
|
||||||
fontSize: 12,
|
style: const TextStyle(
|
||||||
fontWeight: FontWeight.w700,
|
color: AppColors.cream,
|
||||||
height: 1.1,
|
fontSize: AppDimens.caption,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
height: 1.1,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -459,11 +429,13 @@ class _LoginButton extends StatelessWidget {
|
|||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
backgroundColor: AppColors.blue,
|
backgroundColor: AppColors.blue,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 6),
|
padding: const EdgeInsets.symmetric(horizontal: 14),
|
||||||
minimumSize: Size.zero,
|
minimumSize: const Size(AppDimens.tapTarget, AppDimens.tapTarget),
|
||||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
||||||
shape: const StadiumBorder(),
|
shape: const StadiumBorder(),
|
||||||
textStyle: const TextStyle(fontSize: 13, fontWeight: FontWeight.w600),
|
textStyle: const TextStyle(
|
||||||
|
fontSize: AppDimens.body,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
child: Text(label, maxLines: 1),
|
child: Text(label, maxLines: 1),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ class _HomeDramaGrid extends StatelessWidget {
|
|||||||
|
|
||||||
Widget _dramaGrid(List<Drama> dramas) => SliverPadding(
|
Widget _dramaGrid(List<Drama> dramas) => SliverPadding(
|
||||||
key: const Key('home-all-dramas-section'),
|
key: const Key('home-all-dramas-section'),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
padding: const EdgeInsets.symmetric(horizontal: AppDimens.pageHorizontal),
|
||||||
sliver: SliverGrid(
|
sliver: SliverGrid(
|
||||||
key: const Key('home-all-dramas-grid'),
|
key: const Key('home-all-dramas-grid'),
|
||||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
@@ -101,12 +101,12 @@ class _HomeDramaCard extends StatelessWidget {
|
|||||||
return InkWell(
|
return InkWell(
|
||||||
key: Key('home-drama-card-${drama.id}'),
|
key: Key('home-drama-card-${drama.id}'),
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
borderRadius: BorderRadius.circular(14),
|
borderRadius: BorderRadius.circular(AppDimens.cardRadius),
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
key: Key('home-drama-cover-${drama.id}'),
|
key: Key('home-drama-cover-${drama.id}'),
|
||||||
aspectRatio: 2 / 3,
|
aspectRatio: 2 / 3,
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(14),
|
borderRadius: BorderRadius.circular(AppDimens.cardRadius),
|
||||||
child: Stack(
|
child: Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
@@ -143,7 +143,7 @@ class _HomeDramaCard extends StatelessWidget {
|
|||||||
l10n.hot,
|
l10n.hot,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: AppColors.onPrimary,
|
color: AppColors.onPrimary,
|
||||||
fontSize: 10,
|
fontSize: AppDimens.caption,
|
||||||
fontWeight: FontWeight.w800,
|
fontWeight: FontWeight.w800,
|
||||||
height: 1.1,
|
height: 1.1,
|
||||||
),
|
),
|
||||||
@@ -166,7 +166,7 @@ class _HomeDramaCard extends StatelessWidget {
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: AppColors.cream,
|
color: AppColors.cream,
|
||||||
fontSize: 13,
|
fontSize: AppDimens.body,
|
||||||
fontWeight: FontWeight.w800,
|
fontWeight: FontWeight.w800,
|
||||||
height: 1.15,
|
height: 1.15,
|
||||||
),
|
),
|
||||||
@@ -178,8 +178,8 @@ class _HomeDramaCard extends StatelessWidget {
|
|||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Color(0xE6FFFFFF),
|
color: AppColors.cream,
|
||||||
fontSize: 11,
|
fontSize: AppDimens.caption,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
height: 1.2,
|
height: 1.2,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import '../../core/formatting/error_text.dart';
|
|||||||
import '../../core/formatting/localized_date_time.dart';
|
import '../../core/formatting/localized_date_time.dart';
|
||||||
import '../../core/platform/haptics.dart';
|
import '../../core/platform/haptics.dart';
|
||||||
import '../../core/theme/app_colors.dart';
|
import '../../core/theme/app_colors.dart';
|
||||||
|
import '../../core/theme/app_dimens.dart';
|
||||||
import '../../core/theme/app_theme.dart';
|
import '../../core/theme/app_theme.dart';
|
||||||
import '../../core/widgets/auth_gate.dart';
|
import '../../core/widgets/auth_gate.dart';
|
||||||
import '../../core/widgets/empty_state.dart';
|
import '../../core/widgets/empty_state.dart';
|
||||||
@@ -60,9 +61,12 @@ class _DownloadsPageState extends State<DownloadsPage> {
|
|||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
title: Text(l10n.downloads),
|
title: Text(l10n.downloads),
|
||||||
),
|
),
|
||||||
body: signedIn
|
body: SafeArea(
|
||||||
? _DownloadsBody(downloads: context.watch<DownloadController>())
|
top: false,
|
||||||
: const AuthGatePanel(),
|
child: signedIn
|
||||||
|
? _DownloadsBody(downloads: context.watch<DownloadController>())
|
||||||
|
: const AuthGatePanel(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -194,11 +198,11 @@ class _PolicyPanel extends StatelessWidget {
|
|||||||
final l10n = AppLocalizations.of(context)!;
|
final l10n = AppLocalizations.of(context)!;
|
||||||
return Container(
|
return Container(
|
||||||
key: const Key('download-policy'),
|
key: const Key('download-policy'),
|
||||||
padding: const EdgeInsets.fromLTRB(14, 14, 14, 10),
|
padding: const EdgeInsets.fromLTRB(16, 16, 16, 12),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.card,
|
color: AppColors.card,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(AppDimens.innerRadius),
|
||||||
border: Border.all(color: const Color(0xFF2A2C33)),
|
border: Border.all(color: AppColors.outlineVariantDark),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -219,15 +223,21 @@ class _PolicyPanel extends StatelessWidget {
|
|||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
Text(
|
Text(
|
||||||
policy.expirePolicyText,
|
policy.expirePolicyText,
|
||||||
style: const TextStyle(color: Color(0xFFB7B3B8), fontSize: 12),
|
style: const TextStyle(
|
||||||
|
color: AppColors.muted,
|
||||||
|
fontSize: AppDimens.caption,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const Divider(height: 22, color: Color(0xFF2A2C33)),
|
const Divider(height: 22, color: AppColors.outlineVariantDark),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
l10n.downloadWifiOnly,
|
l10n.downloadWifiOnly,
|
||||||
style: const TextStyle(color: Colors.white, fontSize: 13),
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: AppDimens.body,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Switch(
|
Switch(
|
||||||
@@ -242,7 +252,10 @@ class _PolicyPanel extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
l10n.downloadQuality,
|
l10n.downloadQuality,
|
||||||
style: const TextStyle(color: Colors.white, fontSize: 13),
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: AppDimens.body,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
DropdownButton<String>(
|
DropdownButton<String>(
|
||||||
@@ -302,7 +315,7 @@ class _LicenseTile extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(12),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.card,
|
color: AppColors.card,
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(AppDimens.innerRadius),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -355,10 +368,12 @@ class _LicenseTile extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(AppDimens.progressRadius),
|
||||||
child: LinearProgressIndicator(
|
child: LinearProgressIndicator(
|
||||||
minHeight: 4,
|
minHeight: AppDimens.progressHeight,
|
||||||
value: (license.progressPercent / 100).clamp(0.0, 1.0),
|
value: (license.progressPercent / 100).clamp(0.0, 1.0),
|
||||||
|
backgroundColor: Colors.white24,
|
||||||
|
valueColor: const AlwaysStoppedAnimation(AppColors.blueSoft),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (license.expireAt != null) ...[
|
if (license.expireAt != null) ...[
|
||||||
@@ -367,7 +382,10 @@ class _LicenseTile extends StatelessWidget {
|
|||||||
l10n.downloadExpiresAt(
|
l10n.downloadExpiresAt(
|
||||||
formatLocalizedDateTime(context, license.expireAt!),
|
formatLocalizedDateTime(context, license.expireAt!),
|
||||||
),
|
),
|
||||||
style: const TextStyle(color: Color(0xFF8D8990), fontSize: 11),
|
style: const TextStyle(
|
||||||
|
color: AppColors.muted,
|
||||||
|
fontSize: AppDimens.caption,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import '../../core/formatting/error_text.dart';
|
|||||||
import '../../core/formatting/localized_content.dart';
|
import '../../core/formatting/localized_content.dart';
|
||||||
import '../../core/platform/haptics.dart';
|
import '../../core/platform/haptics.dart';
|
||||||
import '../../core/theme/app_colors.dart';
|
import '../../core/theme/app_colors.dart';
|
||||||
|
import '../../core/theme/app_dimens.dart';
|
||||||
import '../../core/theme/app_theme.dart';
|
import '../../core/theme/app_theme.dart';
|
||||||
import '../../core/widgets/remote_image.dart';
|
import '../../core/widgets/remote_image.dart';
|
||||||
import '../../core/widgets/auth_gate.dart';
|
import '../../core/widgets/auth_gate.dart';
|
||||||
@@ -78,14 +79,24 @@ class _DramaDetailPageState extends State<DramaDetailPage> {
|
|||||||
Widget _page(BuildContext context) {
|
Widget _page(BuildContext context) {
|
||||||
final l10n = AppLocalizations.of(context)!;
|
final l10n = AppLocalizations.of(context)!;
|
||||||
if (logic.loading) {
|
if (logic.loading) {
|
||||||
return const Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: AppColors.ink,
|
backgroundColor: AppColors.ink,
|
||||||
body: Center(child: CircularProgressIndicator()),
|
appBar: AppBar(
|
||||||
|
backgroundColor: AppColors.ink,
|
||||||
|
foregroundColor: AppColors.onPrimary,
|
||||||
|
title: Text(localizedDramaTitle(l10n, widget.drama)),
|
||||||
|
),
|
||||||
|
body: const Center(child: CircularProgressIndicator()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (logic.failed) {
|
if (logic.failed) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: AppColors.ink,
|
backgroundColor: AppColors.ink,
|
||||||
|
appBar: AppBar(
|
||||||
|
backgroundColor: AppColors.ink,
|
||||||
|
foregroundColor: AppColors.onPrimary,
|
||||||
|
title: Text(localizedDramaTitle(l10n, widget.drama)),
|
||||||
|
),
|
||||||
body: Center(
|
body: Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 32),
|
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||||
@@ -140,11 +151,25 @@ class _DramaDetailPageState extends State<DramaDetailPage> {
|
|||||||
_favoriteAction(context, l10n),
|
_favoriteAction(context, l10n),
|
||||||
],
|
],
|
||||||
flexibleSpace: FlexibleSpaceBar(
|
flexibleSpace: FlexibleSpaceBar(
|
||||||
background: RemoteImage(drama.posterUrl),
|
background: Stack(
|
||||||
|
fit: StackFit.expand,
|
||||||
|
children: [
|
||||||
|
RemoteImage(drama.posterUrl),
|
||||||
|
const DecoratedBox(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
begin: Alignment.topCenter,
|
||||||
|
end: Alignment.center,
|
||||||
|
colors: [Color(0x99000000), Color(0x00000000)],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SliverPadding(
|
SliverPadding(
|
||||||
padding: const EdgeInsets.fromLTRB(24, 24, 24, 40),
|
padding: const EdgeInsets.fromLTRB(16, 16, 16, 40),
|
||||||
sliver: SliverList(
|
sliver: SliverList(
|
||||||
delegate: SliverChildListDelegate([
|
delegate: SliverChildListDelegate([
|
||||||
Text(
|
Text(
|
||||||
@@ -158,16 +183,17 @@ class _DramaDetailPageState extends State<DramaDetailPage> {
|
|||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(
|
Text(
|
||||||
localizedDramaTitle(l10n, drama),
|
localizedDramaTitle(l10n, drama),
|
||||||
style: Theme.of(context).textTheme.headlineLarge?.copyWith(
|
style: const TextStyle(
|
||||||
|
fontSize: AppDimens.detailHeadline,
|
||||||
fontWeight: FontWeight.w800,
|
fontWeight: FontWeight.w800,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
Text(
|
Text(
|
||||||
localizedDramaSynopsis(l10n, drama),
|
localizedDramaSynopsis(l10n, drama),
|
||||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
style: Theme.of(
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
context,
|
||||||
),
|
).textTheme.bodyLarge?.copyWith(color: AppColors.muted),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
Column(
|
Column(
|
||||||
@@ -222,15 +248,16 @@ class _DramaDetailPageState extends State<DramaDetailPage> {
|
|||||||
const SizedBox(height: 30),
|
const SizedBox(height: 30),
|
||||||
Text(
|
Text(
|
||||||
l10n.episodes,
|
l10n.episodes,
|
||||||
style: Theme.of(
|
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||||
context,
|
fontSize: AppDimens.sectionTitle,
|
||||||
).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SliverPadding(
|
SliverPadding(
|
||||||
padding: const EdgeInsets.fromLTRB(24, 0, 24, 40),
|
padding: const EdgeInsets.fromLTRB(16, 0, 16, 40),
|
||||||
sliver: SliverList.builder(
|
sliver: SliverList.builder(
|
||||||
itemCount: drama.episodes.length,
|
itemCount: drama.episodes.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
@@ -364,10 +391,10 @@ class _ResumeBar extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(2),
|
borderRadius: BorderRadius.circular(AppDimens.progressRadius),
|
||||||
child: LinearProgressIndicator(
|
child: LinearProgressIndicator(
|
||||||
value: fraction,
|
value: fraction,
|
||||||
minHeight: 3,
|
minHeight: AppDimens.progressHeight,
|
||||||
backgroundColor: Colors.white24,
|
backgroundColor: Colors.white24,
|
||||||
valueColor: const AlwaysStoppedAnimation(AppColors.blueSoft),
|
valueColor: const AlwaysStoppedAnimation(AppColors.blueSoft),
|
||||||
),
|
),
|
||||||
@@ -376,7 +403,10 @@ class _ResumeBar extends StatelessWidget {
|
|||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
label!,
|
label!,
|
||||||
style: const TextStyle(color: Colors.white54, fontSize: 11),
|
style: const TextStyle(
|
||||||
|
color: AppColors.muted,
|
||||||
|
fontSize: AppDimens.caption,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -59,9 +59,12 @@ class _FavoritesPageState extends State<FavoritesPage> {
|
|||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
title: Text(l10n.myFavorites),
|
title: Text(l10n.myFavorites),
|
||||||
),
|
),
|
||||||
body: signedIn
|
body: SafeArea(
|
||||||
? _FavoritesBody(favorites: context.watch<FavoriteController>())
|
top: false,
|
||||||
: const AuthGatePanel(),
|
child: signedIn
|
||||||
|
? _FavoritesBody(favorites: context.watch<FavoriteController>())
|
||||||
|
: const AuthGatePanel(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -114,7 +117,7 @@ class _FavoritesBody extends StatelessWidget {
|
|||||||
onPressed: () => _remove(context, drama.id),
|
onPressed: () => _remove(context, drama.id),
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
Icons.favorite_rounded,
|
Icons.favorite_rounded,
|
||||||
color: Color(0xFFD74A7D),
|
color: AppColors.favorite,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
+35
-113
@@ -13,6 +13,7 @@ import '../../core/data/services/catalog_controller.dart';
|
|||||||
import '../../core/platform/haptics.dart';
|
import '../../core/platform/haptics.dart';
|
||||||
import '../../core/theme/app_colors.dart';
|
import '../../core/theme/app_colors.dart';
|
||||||
import '../../core/theme/app_theme.dart';
|
import '../../core/theme/app_theme.dart';
|
||||||
|
import '../../core/widgets/app_bottom_nav.dart';
|
||||||
import '../../core/widgets/auth_gate.dart';
|
import '../../core/widgets/auth_gate.dart';
|
||||||
import '../../core/widgets/empty_state.dart';
|
import '../../core/widgets/empty_state.dart';
|
||||||
import '../../core/widgets/server_endpoint_dialog.dart';
|
import '../../core/widgets/server_endpoint_dialog.dart';
|
||||||
@@ -239,57 +240,42 @@ class _HomeTabsState extends State<_HomeTabs> {
|
|||||||
),
|
),
|
||||||
bottomNavigationBar: logic.videoImmersive
|
bottomNavigationBar: logic.videoImmersive
|
||||||
? null
|
? null
|
||||||
: SafeArea(
|
: AppBottomNav(
|
||||||
top: false,
|
barKey: const Key('home-tabs-bar'),
|
||||||
minimum: const EdgeInsets.only(bottom: 2),
|
items: [
|
||||||
child: Container(
|
AppBottomNavItem(
|
||||||
key: const Key('home-tabs-bar'),
|
tabKey: const Key('tab-discover'),
|
||||||
height: 64,
|
icon: Icons.home_outlined,
|
||||||
decoration: BoxDecoration(
|
selectedIcon: Icons.home_rounded,
|
||||||
color: AppColors.bar,
|
label: l10n.homeTab,
|
||||||
border: Border(
|
selected: logic.tab == 1,
|
||||||
top: BorderSide(
|
onTap: () => _selectTab(context, 1),
|
||||||
color: Theme.of(context).colorScheme.outlineVariant,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
child: Row(
|
AppBottomNavItem(
|
||||||
children: [
|
tabKey: const Key('tab-home'),
|
||||||
_TabButton(
|
icon: Icons.movie_outlined,
|
||||||
key: const Key('tab-discover'),
|
selectedIcon: Icons.movie_rounded,
|
||||||
icon: Icons.home_outlined,
|
label: l10n.shortsTab,
|
||||||
selectedIcon: Icons.home_rounded,
|
selected: logic.tab == 0,
|
||||||
label: l10n.homeTab,
|
onTap: () => _selectTab(context, 0),
|
||||||
selected: logic.tab == 1,
|
|
||||||
onTap: () => _selectTab(context, 1),
|
|
||||||
),
|
|
||||||
_TabButton(
|
|
||||||
key: const Key('tab-home'),
|
|
||||||
icon: Icons.movie_outlined,
|
|
||||||
selectedIcon: Icons.movie_rounded,
|
|
||||||
label: l10n.shortsTab,
|
|
||||||
selected: logic.tab == 0,
|
|
||||||
onTap: () => _selectTab(context, 0),
|
|
||||||
),
|
|
||||||
_TabButton(
|
|
||||||
key: const Key('tab-my-list'),
|
|
||||||
icon: Icons.bookmark_border_rounded,
|
|
||||||
selectedIcon: Icons.bookmark_rounded,
|
|
||||||
label: l10n.myListTab,
|
|
||||||
selected: logic.tab == 2 && !logic.profile,
|
|
||||||
onTap: () => _selectTab(context, 2),
|
|
||||||
),
|
|
||||||
_TabButton(
|
|
||||||
key: const Key('tab-my'),
|
|
||||||
icon: Icons.person_outline_rounded,
|
|
||||||
selectedIcon: Icons.person_rounded,
|
|
||||||
label: l10n.myTab,
|
|
||||||
selected: logic.tab == 2 && logic.profile,
|
|
||||||
onTap: () => _selectTab(context, 2, myProfile: true),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
AppBottomNavItem(
|
||||||
|
tabKey: const Key('tab-my-list'),
|
||||||
|
icon: Icons.bookmark_border_rounded,
|
||||||
|
selectedIcon: Icons.bookmark_rounded,
|
||||||
|
label: l10n.myListTab,
|
||||||
|
selected: logic.tab == 2 && !logic.profile,
|
||||||
|
onTap: () => _selectTab(context, 2),
|
||||||
|
),
|
||||||
|
AppBottomNavItem(
|
||||||
|
tabKey: const Key('tab-my'),
|
||||||
|
icon: Icons.person_outline_rounded,
|
||||||
|
selectedIcon: Icons.person_rounded,
|
||||||
|
label: l10n.myTab,
|
||||||
|
selected: logic.tab == 2 && logic.profile,
|
||||||
|
onTap: () => _selectTab(context, 2, myProfile: true),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -309,67 +295,3 @@ class _HomeTabsState extends State<_HomeTabs> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _TabButton extends StatelessWidget {
|
|
||||||
const _TabButton({
|
|
||||||
super.key,
|
|
||||||
required this.icon,
|
|
||||||
required this.selectedIcon,
|
|
||||||
required this.label,
|
|
||||||
required this.selected,
|
|
||||||
required this.onTap,
|
|
||||||
});
|
|
||||||
final IconData icon;
|
|
||||||
final IconData selectedIcon;
|
|
||||||
final String label;
|
|
||||||
final bool selected;
|
|
||||||
final VoidCallback onTap;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) => Expanded(
|
|
||||||
child: Semantics(
|
|
||||||
button: true,
|
|
||||||
selected: selected,
|
|
||||||
label: label,
|
|
||||||
child: InkWell(
|
|
||||||
onTap: onTap,
|
|
||||||
child: ConstrainedBox(
|
|
||||||
constraints: const BoxConstraints(minHeight: 48),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
AnimatedContainer(
|
|
||||||
duration: const Duration(milliseconds: 160),
|
|
||||||
curve: Curves.easeOut,
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 14,
|
|
||||||
vertical: 3,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: selected ? AppColors.blue : null,
|
|
||||||
borderRadius: BorderRadius.circular(14),
|
|
||||||
),
|
|
||||||
child: Icon(
|
|
||||||
selected ? selectedIcon : icon,
|
|
||||||
size: 22,
|
|
||||||
color: selected ? AppColors.onPrimary : AppColors.muted,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 2),
|
|
||||||
Text(
|
|
||||||
label,
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 10,
|
|
||||||
fontWeight: selected ? FontWeight.w700 : FontWeight.w500,
|
|
||||||
color: selected ? AppColors.blueSoft : AppColors.muted,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -7,6 +7,7 @@ import '../../../core/config/locale_controller.dart';
|
|||||||
import '../../../core/data/models/drama.dart';
|
import '../../../core/data/models/drama.dart';
|
||||||
import '../../../core/formatting/localized_content.dart';
|
import '../../../core/formatting/localized_content.dart';
|
||||||
import '../../../core/theme/app_colors.dart';
|
import '../../../core/theme/app_colors.dart';
|
||||||
|
import '../../../core/theme/app_dimens.dart';
|
||||||
import '../../../core/widgets/remote_image.dart';
|
import '../../../core/widgets/remote_image.dart';
|
||||||
import '../../../l10n/app_localizations.dart';
|
import '../../../l10n/app_localizations.dart';
|
||||||
|
|
||||||
@@ -51,7 +52,7 @@ class HomeQuickDrawer extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
const CircleAvatar(
|
const CircleAvatar(
|
||||||
radius: 30,
|
radius: 30,
|
||||||
backgroundColor: Color(0xFF4B4142),
|
backgroundColor: AppColors.raised,
|
||||||
child: Icon(
|
child: Icon(
|
||||||
Icons.person_rounded,
|
Icons.person_rounded,
|
||||||
size: 34,
|
size: 34,
|
||||||
@@ -74,7 +75,7 @@ class HomeQuickDrawer extends StatelessWidget {
|
|||||||
const SizedBox(height: 3),
|
const SizedBox(height: 3),
|
||||||
Text(
|
Text(
|
||||||
l10n.localDemoProfile,
|
l10n.localDemoProfile,
|
||||||
style: const TextStyle(color: Color(0xFFE7E2E0)),
|
style: const TextStyle(color: AppColors.cream),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -91,7 +92,9 @@ class HomeQuickDrawer extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(
|
||||||
|
AppDimens.innerRadius,
|
||||||
|
),
|
||||||
child: RemoteImage(
|
child: RemoteImage(
|
||||||
drama.posterUrl,
|
drama.posterUrl,
|
||||||
width: 74,
|
width: 74,
|
||||||
@@ -115,7 +118,7 @@ class HomeQuickDrawer extends StatelessWidget {
|
|||||||
const SizedBox(height: 7),
|
const SizedBox(height: 7),
|
||||||
Text(
|
Text(
|
||||||
l10n.episodeCount(drama.episodeCount),
|
l10n.episodeCount(drama.episodeCount),
|
||||||
style: const TextStyle(color: Color(0xFFE7E2E0)),
|
style: const TextStyle(color: AppColors.cream),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -229,7 +232,7 @@ class _SectionCard extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: HomeQuickDrawer._card,
|
color: HomeQuickDrawer._card,
|
||||||
borderRadius: BorderRadius.circular(18),
|
borderRadius: BorderRadius.circular(AppDimens.cardRadius),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -259,11 +262,11 @@ class _QuickAction extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => Material(
|
Widget build(BuildContext context) => Material(
|
||||||
color: const Color(0xFF3B3B3F),
|
color: AppColors.raised,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(AppDimens.innerRadius),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(AppDimens.innerRadius),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 7),
|
padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 7),
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|||||||
+103
-125
@@ -14,8 +14,10 @@ import '../../core/data/services/interaction_controller.dart';
|
|||||||
import '../../core/data/services/favorite_controller.dart';
|
import '../../core/data/services/favorite_controller.dart';
|
||||||
import '../../core/formatting/error_text.dart';
|
import '../../core/formatting/error_text.dart';
|
||||||
import '../../core/widgets/empty_state.dart';
|
import '../../core/widgets/empty_state.dart';
|
||||||
|
import '../../core/widgets/app_bottom_nav.dart';
|
||||||
import '../../core/widgets/remote_image.dart';
|
import '../../core/widgets/remote_image.dart';
|
||||||
import '../../core/theme/app_colors.dart';
|
import '../../core/theme/app_colors.dart';
|
||||||
|
import '../../core/theme/app_dimens.dart';
|
||||||
import '../../core/theme/app_theme.dart';
|
import '../../core/theme/app_theme.dart';
|
||||||
import '../../l10n/app_localizations.dart';
|
import '../../l10n/app_localizations.dart';
|
||||||
import 'mine_binding.dart';
|
import 'mine_binding.dart';
|
||||||
@@ -139,9 +141,9 @@ class _MyPageState extends State<MyPage> {
|
|||||||
Center(
|
Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
l10n.brandMark,
|
l10n.brandMark,
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
color: Color(0xFFD2CDD0),
|
color: AppColors.muted,
|
||||||
fontSize: 10,
|
fontSize: AppDimens.caption,
|
||||||
letterSpacing: 1.3,
|
letterSpacing: 1.3,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -173,7 +175,10 @@ class _MyPageState extends State<MyPage> {
|
|||||||
const SizedBox(width: 7),
|
const SizedBox(width: 7),
|
||||||
Text(
|
Text(
|
||||||
l10n.myListTab,
|
l10n.myListTab,
|
||||||
style: const TextStyle(fontSize: 22, fontWeight: FontWeight.w800),
|
style: const TextStyle(
|
||||||
|
fontSize: AppDimens.pageTitle,
|
||||||
|
fontWeight: FontWeight.w800,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
IconButton(
|
IconButton(
|
||||||
@@ -212,7 +217,7 @@ class _MyPageState extends State<MyPage> {
|
|||||||
l10n.preferencesHeading,
|
l10n.preferencesHeading,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: AppColors.muted,
|
color: AppColors.muted,
|
||||||
fontSize: 12,
|
fontSize: AppDimens.caption,
|
||||||
fontWeight: FontWeight.w800,
|
fontWeight: FontWeight.w800,
|
||||||
letterSpacing: 1.1,
|
letterSpacing: 1.1,
|
||||||
),
|
),
|
||||||
@@ -316,10 +321,10 @@ class _MyPageState extends State<MyPage> {
|
|||||||
|
|
||||||
Widget _wallet(AppLocalizations l10n) {
|
Widget _wallet(AppLocalizations l10n) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(14),
|
padding: const EdgeInsets.all(AppDimens.cardPadding),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.card,
|
color: AppColors.card,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(AppDimens.cardRadius),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -374,7 +379,7 @@ class _MyPageState extends State<MyPage> {
|
|||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(12),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.ink,
|
color: AppColors.ink,
|
||||||
borderRadius: BorderRadius.circular(14),
|
borderRadius: BorderRadius.circular(AppDimens.cardRadius),
|
||||||
border: Border.all(color: color.withValues(alpha: 0.45)),
|
border: Border.all(color: color.withValues(alpha: 0.45)),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -396,7 +401,6 @@ class _MyPageState extends State<MyPage> {
|
|||||||
foregroundColor: color == AppColors.blue
|
foregroundColor: color == AppColors.blue
|
||||||
? Colors.white
|
? Colors.white
|
||||||
: AppColors.onGold,
|
: AppColors.onGold,
|
||||||
visualDensity: VisualDensity.compact,
|
|
||||||
shape: const StadiumBorder(),
|
shape: const StadiumBorder(),
|
||||||
),
|
),
|
||||||
child: Text(action, maxLines: 1, overflow: TextOverflow.ellipsis),
|
child: Text(action, maxLines: 1, overflow: TextOverflow.ellipsis),
|
||||||
@@ -417,6 +421,7 @@ class _MyPageState extends State<MyPage> {
|
|||||||
ListTile(
|
ListTile(
|
||||||
dense: true,
|
dense: true,
|
||||||
visualDensity: VisualDensity.compact,
|
visualDensity: VisualDensity.compact,
|
||||||
|
minTileHeight: AppDimens.tapTarget,
|
||||||
leading: const Icon(
|
leading: const Icon(
|
||||||
Icons.card_giftcard_rounded,
|
Icons.card_giftcard_rounded,
|
||||||
color: AppColors.blueSoft,
|
color: AppColors.blueSoft,
|
||||||
@@ -432,6 +437,7 @@ class _MyPageState extends State<MyPage> {
|
|||||||
ListTile(
|
ListTile(
|
||||||
dense: true,
|
dense: true,
|
||||||
visualDensity: VisualDensity.compact,
|
visualDensity: VisualDensity.compact,
|
||||||
|
minTileHeight: AppDimens.tapTarget,
|
||||||
key: const Key('mine-notices'),
|
key: const Key('mine-notices'),
|
||||||
leading: const Icon(
|
leading: const Icon(
|
||||||
Icons.notifications_none_rounded,
|
Icons.notifications_none_rounded,
|
||||||
@@ -447,6 +453,7 @@ class _MyPageState extends State<MyPage> {
|
|||||||
ListTile(
|
ListTile(
|
||||||
dense: true,
|
dense: true,
|
||||||
visualDensity: VisualDensity.compact,
|
visualDensity: VisualDensity.compact,
|
||||||
|
minTileHeight: AppDimens.tapTarget,
|
||||||
leading: const Icon(
|
leading: const Icon(
|
||||||
Icons.history_rounded,
|
Icons.history_rounded,
|
||||||
color: AppColors.blueSoft,
|
color: AppColors.blueSoft,
|
||||||
@@ -461,6 +468,7 @@ class _MyPageState extends State<MyPage> {
|
|||||||
ListTile(
|
ListTile(
|
||||||
dense: true,
|
dense: true,
|
||||||
visualDensity: VisualDensity.compact,
|
visualDensity: VisualDensity.compact,
|
||||||
|
minTileHeight: AppDimens.tapTarget,
|
||||||
leading: const Icon(
|
leading: const Icon(
|
||||||
Icons.favorite_outline_rounded,
|
Icons.favorite_outline_rounded,
|
||||||
color: AppColors.blueSoft,
|
color: AppColors.blueSoft,
|
||||||
@@ -475,6 +483,7 @@ class _MyPageState extends State<MyPage> {
|
|||||||
ListTile(
|
ListTile(
|
||||||
dense: true,
|
dense: true,
|
||||||
visualDensity: VisualDensity.compact,
|
visualDensity: VisualDensity.compact,
|
||||||
|
minTileHeight: AppDimens.tapTarget,
|
||||||
leading: const Icon(
|
leading: const Icon(
|
||||||
Icons.download_outlined,
|
Icons.download_outlined,
|
||||||
color: AppColors.blueSoft,
|
color: AppColors.blueSoft,
|
||||||
@@ -489,6 +498,7 @@ class _MyPageState extends State<MyPage> {
|
|||||||
ListTile(
|
ListTile(
|
||||||
dense: true,
|
dense: true,
|
||||||
visualDensity: VisualDensity.compact,
|
visualDensity: VisualDensity.compact,
|
||||||
|
minTileHeight: AppDimens.tapTarget,
|
||||||
leading: const Icon(
|
leading: const Icon(
|
||||||
Icons.settings_rounded,
|
Icons.settings_rounded,
|
||||||
color: AppColors.blueSoft,
|
color: AppColors.blueSoft,
|
||||||
@@ -534,30 +544,36 @@ class _MyPageState extends State<MyPage> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
for (var index = 0; index < tabs.length; index++) ...[
|
for (var index = 0; index < tabs.length; index++) ...[
|
||||||
InkWell(
|
Semantics(
|
||||||
onTap: () {
|
selected: logic.section == index,
|
||||||
logic.selectSection(index);
|
button: true,
|
||||||
// 打开收藏页签时以服务端为准刷新一次(多设备/多入口都能对齐)
|
child: InkWell(
|
||||||
if (index == 1) unawaited(favorites.load());
|
onTap: () {
|
||||||
},
|
logic.selectSection(index);
|
||||||
borderRadius: BorderRadius.circular(20),
|
// 打开收藏页签时以服务端为准刷新一次(多设备/多入口都能对齐)
|
||||||
child: Container(
|
if (index == 1) unawaited(favorites.load());
|
||||||
padding: const EdgeInsets.symmetric(
|
},
|
||||||
horizontal: 14,
|
borderRadius: BorderRadius.circular(AppDimens.pillRadius),
|
||||||
vertical: 8,
|
child: Container(
|
||||||
),
|
padding: const EdgeInsets.symmetric(
|
||||||
decoration: BoxDecoration(
|
horizontal: 14,
|
||||||
color: logic.section == index
|
vertical: 8,
|
||||||
? AppColors.blue
|
),
|
||||||
: AppColors.raised,
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(20),
|
color: logic.section == index
|
||||||
),
|
? AppColors.blue
|
||||||
child: Text(
|
: AppColors.raised,
|
||||||
tabs[index],
|
borderRadius: BorderRadius.circular(
|
||||||
style: const TextStyle(
|
AppDimens.pillRadius,
|
||||||
color: Colors.white,
|
),
|
||||||
fontSize: 14,
|
),
|
||||||
fontWeight: FontWeight.w700,
|
child: Text(
|
||||||
|
tabs[index],
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: AppDimens.body,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -581,14 +597,14 @@ class _MyPageState extends State<MyPage> {
|
|||||||
selected: logic.filter == index,
|
selected: logic.filter == index,
|
||||||
onSelected: (_) => logic.selectFilter(index),
|
onSelected: (_) => logic.selectFilter(index),
|
||||||
showCheckmark: false,
|
showCheckmark: false,
|
||||||
visualDensity: VisualDensity.compact,
|
materialTapTargetSize: MaterialTapTargetSize.padded,
|
||||||
backgroundColor: AppColors.raised,
|
backgroundColor: AppColors.raised,
|
||||||
selectedColor: AppColors.blue,
|
selectedColor: AppColors.blue,
|
||||||
labelStyle: TextStyle(
|
labelStyle: TextStyle(
|
||||||
color: logic.filter == index
|
color: logic.filter == index
|
||||||
? Colors.white
|
? Colors.white
|
||||||
: const Color(0xFFD5CED0),
|
: AppColors.muted,
|
||||||
fontSize: 11,
|
fontSize: AppDimens.caption,
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
),
|
),
|
||||||
side: BorderSide.none,
|
side: BorderSide.none,
|
||||||
@@ -606,12 +622,12 @@ class _MyPageState extends State<MyPage> {
|
|||||||
embedded: widget.embedded,
|
embedded: widget.embedded,
|
||||||
onAction: widget.onAction,
|
onAction: widget.onAction,
|
||||||
),
|
),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(AppDimens.innerRadius),
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(9),
|
padding: const EdgeInsets.all(AppDimens.innerPadding),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.card,
|
color: AppColors.card,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(AppDimens.innerRadius),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
@@ -626,7 +642,7 @@ class _MyPageState extends State<MyPage> {
|
|||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 15,
|
fontSize: AppDimens.listTitle,
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -634,8 +650,8 @@ class _MyPageState extends State<MyPage> {
|
|||||||
Text(
|
Text(
|
||||||
l10n.dramaTag,
|
l10n.dramaTag,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Color(0xFFE6E1E3),
|
color: AppColors.cream,
|
||||||
fontSize: 12,
|
fontSize: AppDimens.caption,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 32),
|
const SizedBox(height: 32),
|
||||||
@@ -645,7 +661,7 @@ class _MyPageState extends State<MyPage> {
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: AppColors.blueSoft,
|
color: AppColors.blueSoft,
|
||||||
fontSize: 10,
|
fontSize: AppDimens.caption,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -703,7 +719,7 @@ class _MyPageState extends State<MyPage> {
|
|||||||
l10n.history,
|
l10n.history,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
fontSize: 14,
|
fontSize: AppDimens.body,
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -714,7 +730,7 @@ class _MyPageState extends State<MyPage> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
l10n.progressError,
|
l10n.progressError,
|
||||||
style: const TextStyle(color: Color(0xFFE6E1E3)),
|
style: const TextStyle(color: AppColors.cream),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
@@ -740,7 +756,7 @@ class _MyPageState extends State<MyPage> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
errorText(l10n, logic.recentError, l10n.progressError),
|
errorText(l10n, logic.recentError, l10n.progressError),
|
||||||
style: const TextStyle(color: Color(0xFFE6E1E3)),
|
style: const TextStyle(color: AppColors.cream),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
@@ -794,17 +810,17 @@ class _MyPageState extends State<MyPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(AppDimens.innerRadius),
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(9),
|
padding: const EdgeInsets.all(AppDimens.innerPadding),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.card,
|
color: AppColors.card,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(AppDimens.innerRadius),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(AppDimens.innerRadius),
|
||||||
child: RemoteImage(drama.posterUrl, width: 74, height: 103),
|
child: RemoteImage(drama.posterUrl, width: 74, height: 103),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 11),
|
const SizedBox(width: 11),
|
||||||
@@ -817,7 +833,7 @@ class _MyPageState extends State<MyPage> {
|
|||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 15,
|
fontSize: AppDimens.listTitle,
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -825,56 +841,54 @@ class _MyPageState extends State<MyPage> {
|
|||||||
Text(
|
Text(
|
||||||
l10n.episodeCount(drama.episodeCount),
|
l10n.episodeCount(drama.episodeCount),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Color(0xFFE6E1E3),
|
color: AppColors.cream,
|
||||||
fontSize: 12,
|
fontSize: AppDimens.caption,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Icon(Icons.chevron_right_rounded, color: Color(0xFFF2EEEF)),
|
const Icon(Icons.chevron_right_rounded, color: AppColors.cream),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _bottomNav(AppLocalizations l10n) => Container(
|
Widget _bottomNav(AppLocalizations l10n) => AppBottomNav(
|
||||||
height: 63,
|
items: [
|
||||||
decoration: const BoxDecoration(
|
AppBottomNavItem(
|
||||||
color: AppColors.bar,
|
tabKey: const Key('mine-nav-home'),
|
||||||
border: Border(top: BorderSide(color: AppColors.outlineVariantDark)),
|
icon: Icons.play_circle_outline_rounded,
|
||||||
),
|
selectedIcon: Icons.play_circle_fill_rounded,
|
||||||
child: Row(
|
label: l10n.homeTab,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
onTap: () => logic.act(
|
||||||
children: [
|
context,
|
||||||
_NavItem(
|
MyPageAction.home,
|
||||||
icon: Icons.play_circle_outline_rounded,
|
embedded: widget.embedded,
|
||||||
label: l10n.homeTab,
|
onAction: widget.onAction,
|
||||||
onTap: () => logic.act(
|
|
||||||
context,
|
|
||||||
MyPageAction.home,
|
|
||||||
embedded: widget.embedded,
|
|
||||||
onAction: widget.onAction,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
_NavItem(
|
),
|
||||||
icon: Icons.explore_outlined,
|
AppBottomNavItem(
|
||||||
label: l10n.discoverTab,
|
tabKey: const Key('mine-nav-discover'),
|
||||||
onTap: () => logic.act(
|
icon: Icons.explore_outlined,
|
||||||
context,
|
selectedIcon: Icons.explore_rounded,
|
||||||
MyPageAction.discover,
|
label: l10n.discoverTab,
|
||||||
embedded: widget.embedded,
|
onTap: () => logic.act(
|
||||||
onAction: widget.onAction,
|
context,
|
||||||
),
|
MyPageAction.discover,
|
||||||
|
embedded: widget.embedded,
|
||||||
|
onAction: widget.onAction,
|
||||||
),
|
),
|
||||||
_NavItem(
|
),
|
||||||
icon: Icons.video_library_outlined,
|
AppBottomNavItem(
|
||||||
label: l10n.myTab,
|
tabKey: const Key('mine-nav-current'),
|
||||||
selected: true,
|
icon: Icons.video_library_outlined,
|
||||||
onTap: () {},
|
selectedIcon: Icons.video_library_rounded,
|
||||||
),
|
label: l10n.myTab,
|
||||||
],
|
selected: true,
|
||||||
),
|
onTap: () {},
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _poster({required double width, required double height}) => ClipRRect(
|
Widget _poster({required double width, required double height}) => ClipRRect(
|
||||||
@@ -883,42 +897,6 @@ class _MyPageState extends State<MyPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class _NavItem extends StatelessWidget {
|
|
||||||
const _NavItem({
|
|
||||||
required this.icon,
|
|
||||||
required this.label,
|
|
||||||
required this.onTap,
|
|
||||||
this.selected = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
final IconData icon;
|
|
||||||
final String label;
|
|
||||||
final VoidCallback onTap;
|
|
||||||
final bool selected;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) => InkWell(
|
|
||||||
onTap: onTap,
|
|
||||||
child: SizedBox(
|
|
||||||
width: 72,
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Icon(icon, size: 21, color: selected ? _accent : AppColors.muted),
|
|
||||||
Text(
|
|
||||||
label,
|
|
||||||
style: TextStyle(
|
|
||||||
color: selected ? _accent : AppColors.muted,
|
|
||||||
fontSize: 10,
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
String _clock(Duration duration) {
|
String _clock(Duration duration) {
|
||||||
final seconds = duration.inSeconds;
|
final seconds = duration.inSeconds;
|
||||||
return '${(seconds ~/ 60).toString().padLeft(2, '0')}:${(seconds % 60).toString().padLeft(2, '0')}';
|
return '${(seconds ~/ 60).toString().padLeft(2, '0')}:${(seconds % 60).toString().padLeft(2, '0')}';
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import '../../core/data/services/notices_controller.dart';
|
|||||||
import '../../core/formatting/error_text.dart';
|
import '../../core/formatting/error_text.dart';
|
||||||
import '../../core/formatting/localized_date_time.dart';
|
import '../../core/formatting/localized_date_time.dart';
|
||||||
import '../../core/theme/app_colors.dart';
|
import '../../core/theme/app_colors.dart';
|
||||||
|
import '../../core/theme/app_dimens.dart';
|
||||||
import '../../core/theme/app_theme.dart';
|
import '../../core/theme/app_theme.dart';
|
||||||
import '../../core/widgets/empty_state.dart';
|
import '../../core/widgets/empty_state.dart';
|
||||||
import '../../l10n/app_localizations.dart';
|
import '../../l10n/app_localizations.dart';
|
||||||
@@ -52,9 +53,12 @@ class _NoticesPageState extends State<NoticesPage> {
|
|||||||
foregroundColor: AppColors.onPrimary,
|
foregroundColor: AppColors.onPrimary,
|
||||||
title: Text(l10n.notices),
|
title: Text(l10n.notices),
|
||||||
),
|
),
|
||||||
body: ListenableBuilder(
|
body: SafeArea(
|
||||||
listenable: _controller,
|
top: false,
|
||||||
builder: (context, _) => _body(l10n),
|
child: ListenableBuilder(
|
||||||
|
listenable: _controller,
|
||||||
|
builder: (context, _) => _body(l10n),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -214,12 +218,12 @@ class _NoticeTile extends StatelessWidget {
|
|||||||
return Material(
|
return Material(
|
||||||
key: ValueKey('notice-item-${notice.id}'),
|
key: ValueKey('notice-item-${notice.id}'),
|
||||||
color: AppColors.card,
|
color: AppColors.card,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(AppDimens.innerRadius),
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(14),
|
padding: const EdgeInsets.all(AppDimens.innerPadding),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@@ -233,7 +237,9 @@ class _NoticeTile extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.bluePressed,
|
color: AppColors.bluePressed,
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(
|
||||||
|
AppDimens.pillRadius,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
type,
|
type,
|
||||||
@@ -241,7 +247,7 @@ class _NoticeTile extends StatelessWidget {
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: AppColors.onPrimary,
|
color: AppColors.onPrimary,
|
||||||
fontSize: 11,
|
fontSize: AppDimens.caption,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -257,7 +263,7 @@ class _NoticeTile extends StatelessWidget {
|
|||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: AppColors.muted,
|
color: AppColors.muted,
|
||||||
fontSize: 11,
|
fontSize: AppDimens.caption,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -271,7 +277,7 @@ class _NoticeTile extends StatelessWidget {
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: AppColors.onPrimary,
|
color: AppColors.onPrimary,
|
||||||
fontSize: 16,
|
fontSize: AppDimens.sectionTitle,
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -283,7 +289,7 @@ class _NoticeTile extends StatelessWidget {
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: AppColors.muted,
|
color: AppColors.muted,
|
||||||
fontSize: 13,
|
fontSize: AppDimens.body,
|
||||||
height: 1.35,
|
height: 1.35,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import '../../core/formatting/localized_content.dart';
|
|||||||
import '../../core/platform/haptics.dart';
|
import '../../core/platform/haptics.dart';
|
||||||
import '../../core/theme/app_colors.dart';
|
import '../../core/theme/app_colors.dart';
|
||||||
import '../../core/theme/app_theme.dart';
|
import '../../core/theme/app_theme.dart';
|
||||||
|
import '../../core/widgets/app_bottom_nav.dart';
|
||||||
import '../../core/widgets/auth_gate.dart';
|
import '../../core/widgets/auth_gate.dart';
|
||||||
import '../../core/widgets/download_action.dart';
|
import '../../core/widgets/download_action.dart';
|
||||||
import '../../l10n/app_localizations.dart';
|
import '../../l10n/app_localizations.dart';
|
||||||
@@ -734,14 +735,14 @@ class _PlaybackOverlay extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
_RailAction(
|
_RailAction(
|
||||||
key: const Key('rail-like'),
|
key: const Key('rail-like'),
|
||||||
icon: Icons.favorite_border,
|
icon: liked ? Icons.favorite : Icons.favorite_border,
|
||||||
label: l10n.like,
|
label: l10n.like,
|
||||||
selected: liked,
|
selected: liked,
|
||||||
onTap: onLike,
|
onTap: onLike,
|
||||||
),
|
),
|
||||||
_RailAction(
|
_RailAction(
|
||||||
key: const Key('rail-save'),
|
key: const Key('rail-save'),
|
||||||
icon: Icons.bookmark_border,
|
icon: saved ? Icons.bookmark : Icons.bookmark_border,
|
||||||
label: l10n.save,
|
label: l10n.save,
|
||||||
selected: saved,
|
selected: saved,
|
||||||
onTap: onSave,
|
onTap: onSave,
|
||||||
@@ -787,30 +788,31 @@ class _PlaybackOverlay extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _fallbackNav() => Container(
|
Widget _fallbackNav() => AppBottomNav(
|
||||||
height: 62,
|
items: [
|
||||||
color: const Color(0xF20C0C0F),
|
AppBottomNavItem(
|
||||||
child: Row(
|
tabKey: const Key('player-nav-home'),
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
icon: Icons.play_circle_outline_rounded,
|
||||||
children: [
|
selectedIcon: Icons.play_circle_fill_rounded,
|
||||||
_BottomAction(
|
label: l10n.homeTab,
|
||||||
icon: Icons.play_circle_outline_rounded,
|
selected: true,
|
||||||
label: l10n.homeTab,
|
onTap: () {},
|
||||||
selected: true,
|
),
|
||||||
onTap: () {},
|
AppBottomNavItem(
|
||||||
),
|
tabKey: const Key('player-nav-discover'),
|
||||||
_BottomAction(
|
icon: Icons.explore_outlined,
|
||||||
icon: Icons.explore_outlined,
|
selectedIcon: Icons.explore_rounded,
|
||||||
label: l10n.discoverTab,
|
label: l10n.discoverTab,
|
||||||
onTap: onDiscover,
|
onTap: onDiscover,
|
||||||
),
|
),
|
||||||
_BottomAction(
|
AppBottomNavItem(
|
||||||
icon: Icons.video_library_outlined,
|
tabKey: const Key('player-nav-my'),
|
||||||
label: l10n.myTab,
|
icon: Icons.video_library_outlined,
|
||||||
onTap: onMy,
|
selectedIcon: Icons.video_library_rounded,
|
||||||
),
|
label: l10n.myTab,
|
||||||
],
|
onTap: onMy,
|
||||||
),
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -844,43 +846,3 @@ class _RailAction extends StatelessWidget {
|
|||||||
icon: Icon(icon, size: _railIconSize, weight: _railIconWeight, fill: 0),
|
icon: Icon(icon, size: _railIconSize, weight: _railIconWeight, fill: 0),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class _BottomAction extends StatelessWidget {
|
|
||||||
const _BottomAction({
|
|
||||||
required this.icon,
|
|
||||||
required this.label,
|
|
||||||
required this.onTap,
|
|
||||||
this.selected = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
final IconData icon;
|
|
||||||
final String label;
|
|
||||||
final VoidCallback onTap;
|
|
||||||
final bool selected;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) => InkWell(
|
|
||||||
onTap: onTap,
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 6),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
icon,
|
|
||||||
color: selected ? AppColors.blueSoft : const Color(0xFFF3EEEA),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 2),
|
|
||||||
Text(
|
|
||||||
label,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 11,
|
|
||||||
color: selected ? AppColors.blueSoft : const Color(0xFFF3EEEA),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
|
|||||||
import '../../../core/data/models/comment.dart';
|
import '../../../core/data/models/comment.dart';
|
||||||
import '../../../core/data/services/comment_controller.dart';
|
import '../../../core/data/services/comment_controller.dart';
|
||||||
import '../../../core/theme/app_colors.dart';
|
import '../../../core/theme/app_colors.dart';
|
||||||
|
import '../../../core/theme/app_dimens.dart';
|
||||||
import '../../../l10n/app_localizations.dart';
|
import '../../../l10n/app_localizations.dart';
|
||||||
|
|
||||||
const String _deleteAction = 'delete';
|
const String _deleteAction = 'delete';
|
||||||
@@ -43,7 +44,9 @@ class CommentTile extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final l10n = AppLocalizations.of(context)!;
|
final l10n = AppLocalizations.of(context)!;
|
||||||
final name = comment.nickname.isEmpty ? l10n.anonymousUser : comment.nickname;
|
final name = comment.nickname.isEmpty
|
||||||
|
? l10n.anonymousUser
|
||||||
|
: comment.nickname;
|
||||||
return Padding(
|
return Padding(
|
||||||
key: Key('comment-item-${comment.id}'),
|
key: Key('comment-item-${comment.id}'),
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||||
@@ -111,7 +114,7 @@ class CommentTile extends StatelessWidget {
|
|||||||
key: Key('comment-pending-${comment.id}'),
|
key: Key('comment-pending-${comment.id}'),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: AppColors.blueSoft,
|
color: AppColors.blueSoft,
|
||||||
fontSize: 11,
|
fontSize: AppDimens.caption,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import '../../core/formatting/error_text.dart';
|
|||||||
import '../../core/formatting/localized_date_time.dart';
|
import '../../core/formatting/localized_date_time.dart';
|
||||||
import '../../core/platform/haptics.dart';
|
import '../../core/platform/haptics.dart';
|
||||||
import '../../core/theme/app_colors.dart';
|
import '../../core/theme/app_colors.dart';
|
||||||
|
import '../../core/theme/app_dimens.dart';
|
||||||
import '../../core/theme/app_theme.dart';
|
import '../../core/theme/app_theme.dart';
|
||||||
import '../../core/widgets/auth_gate.dart';
|
import '../../core/widgets/auth_gate.dart';
|
||||||
import '../../core/widgets/empty_state.dart';
|
import '../../core/widgets/empty_state.dart';
|
||||||
@@ -41,7 +42,10 @@ class WatchRecordsPage extends StatelessWidget {
|
|||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
title: Text(l10n.watchHistory),
|
title: Text(l10n.watchHistory),
|
||||||
),
|
),
|
||||||
body: signedIn ? const _WatchRecordsBody() : const AuthGatePanel(),
|
body: SafeArea(
|
||||||
|
top: false,
|
||||||
|
child: signedIn ? const _WatchRecordsBody() : const AuthGatePanel(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -207,16 +211,19 @@ class _RecordTile extends StatelessWidget {
|
|||||||
if (drama == null || record.detailLoading) {
|
if (drama == null || record.detailLoading) {
|
||||||
return Container(
|
return Container(
|
||||||
key: ValueKey('watch-record-${record.dramaId}'),
|
key: ValueKey('watch-record-${record.dramaId}'),
|
||||||
height: 112,
|
height: AppDimens.watchPlaceholderHeight,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.card,
|
color: AppColors.card,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(AppDimens.innerRadius),
|
||||||
),
|
),
|
||||||
child: const Center(
|
child: const Padding(
|
||||||
child: SizedBox(
|
padding: EdgeInsets.all(AppDimens.innerPadding),
|
||||||
width: 22,
|
child: Center(
|
||||||
height: 22,
|
child: SizedBox(
|
||||||
child: CircularProgressIndicator(strokeWidth: 2),
|
width: 22,
|
||||||
|
height: 22,
|
||||||
|
child: CircularProgressIndicator(strokeWidth: 2),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -227,12 +234,12 @@ class _RecordTile extends StatelessWidget {
|
|||||||
return Material(
|
return Material(
|
||||||
key: ValueKey('watch-record-${record.dramaId}'),
|
key: ValueKey('watch-record-${record.dramaId}'),
|
||||||
color: AppColors.card,
|
color: AppColors.card,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(AppDimens.innerRadius),
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: onOpen,
|
onTap: onOpen,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(9),
|
padding: const EdgeInsets.all(AppDimens.innerPadding),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
@@ -250,7 +257,7 @@ class _RecordTile extends StatelessWidget {
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
fontSize: 15,
|
fontSize: AppDimens.listTitle,
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -260,15 +267,21 @@ class _RecordTile extends StatelessWidget {
|
|||||||
key: ValueKey('watch-record-episode-${record.dramaId}'),
|
key: ValueKey('watch-record-episode-${record.dramaId}'),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: AppColors.muted,
|
color: AppColors.muted,
|
||||||
fontSize: 12,
|
fontSize: AppDimens.caption,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 7),
|
const SizedBox(height: 7),
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(
|
||||||
|
AppDimens.progressRadius,
|
||||||
|
),
|
||||||
child: LinearProgressIndicator(
|
child: LinearProgressIndicator(
|
||||||
minHeight: 4,
|
minHeight: AppDimens.progressHeight,
|
||||||
value: record.progress.fraction,
|
value: record.progress.fraction,
|
||||||
|
backgroundColor: Colors.white24,
|
||||||
|
valueColor: const AlwaysStoppedAnimation(
|
||||||
|
AppColors.blueSoft,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
@@ -279,7 +292,7 @@ class _RecordTile extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: AppColors.blueSoft,
|
color: AppColors.blueSoft,
|
||||||
fontSize: 11,
|
fontSize: AppDimens.caption,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (record.progress.updatedAt != null)
|
if (record.progress.updatedAt != null)
|
||||||
@@ -292,8 +305,8 @@ class _RecordTile extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
key: ValueKey('watch-record-updated-${record.dramaId}'),
|
key: ValueKey('watch-record-updated-${record.dramaId}'),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Color(0xFF8D8990),
|
color: AppColors.muted,
|
||||||
fontSize: 11,
|
fontSize: AppDimens.caption,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -324,10 +337,10 @@ class _FailedRecordTile extends StatelessWidget {
|
|||||||
final l10n = AppLocalizations.of(context)!;
|
final l10n = AppLocalizations.of(context)!;
|
||||||
return Container(
|
return Container(
|
||||||
key: ValueKey('watch-record-failed-${record.dramaId}'),
|
key: ValueKey('watch-record-failed-${record.dramaId}'),
|
||||||
padding: const EdgeInsets.all(14),
|
padding: const EdgeInsets.all(AppDimens.innerPadding),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.card,
|
color: AppColors.card,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(AppDimens.innerRadius),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
Reference in new issue
Block a user