From 87db5485910f4e7728b817f00bed3d91de257c7c Mon Sep 17 00:00:00 2001 From: Zhou Weidong <502333560@qq.com> Date: Mon, 19 Oct 2026 15:50:07 +0800 Subject: [PATCH] =?UTF-8?q?feat(theme):=20=E6=A0=A1=E6=AD=A3=20K26=20?= =?UTF-8?q?=E9=A2=84=E8=A7=88=E8=89=B2=E6=9D=BF=E4=B8=8E=E5=AF=BC=E8=88=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/theme/app_colors.dart | 23 +++++++---- lib/core/theme/app_dimens.dart | 32 +++++++++----- lib/core/widgets/app_bottom_nav.dart | 20 ++------- lib/core/widgets/glass_panel.dart | 2 +- lib/l10n/app_en.arb | 23 ++++++++++- lib/l10n/app_id.arb | 23 ++++++++++- lib/l10n/app_localizations.dart | 62 +++++++++++++++++++++++++++- lib/l10n/app_localizations_en.dart | 38 ++++++++++++++++- lib/l10n/app_localizations_id.dart | 38 ++++++++++++++++- lib/l10n/app_localizations_zh.dart | 38 ++++++++++++++++- lib/l10n/app_zh.arb | 25 ++++++++++- lib/pages/home/home_view.dart | 11 ++--- test/theme/app_dimens_test.dart | 10 ++--- test/theme/app_theme_test.dart | 6 ++- 14 files changed, 293 insertions(+), 58 deletions(-) diff --git a/lib/core/theme/app_colors.dart b/lib/core/theme/app_colors.dart index beca1ee..da46392 100644 --- a/lib/core/theme/app_colors.dart +++ b/lib/core/theme/app_colors.dart @@ -38,25 +38,34 @@ abstract final class AppColors { static const ink = Color(0xFF0B0C10); /// Bottom navigation background (rendered at 95% with blur). - static const bar = Color(0xFF0B0C10); + static const bar = Color(0xFF0D0D11); /// Cards lifted off [ink]. - static const card = Color(0xFF16181F); + static const card = Color(0xFF1B1C21); + + /// Alternate card surface from the mobile preview. + static const cardAlt = Color(0xFF1C1D21); /// Floating surfaces, inputs, and nested rows. - static const raised = Color(0xFF1D1E28); + static const raised = Color(0xFF1C1D21); + static const quick = Color(0xFF202126); + static const input = Color(0xFF25252A); + static const goldCard = Color(0xFF201B1E); /// Body text on dark surfaces. - static const cream = Color(0xFFE3E2E8); + static const cream = Color(0xFFE6E1E3); /// Titles and high-emphasis text. static const onSurface = Color(0xFFFFFFFF); /// Secondary text. - static const muted = Color(0xFFA1A1AA); + static const muted = Color(0xFFD2CDD0); /// Weak hints and timestamps. - static const subtle = Color(0xFF71717A); + static const subtle = Color(0xFF9A96A3); + + /// Soft orange used for continue labels and secondary times. + static const accent = Color(0xFFFFA490); /// 8% white border used by cards and inputs. static const border = Color(0x14FFFFFF); @@ -65,7 +74,7 @@ abstract final class AppColors { static const glassBorder = Color(0x24FFFFFF); static const outlineDark = Color(0xFF2A2C36); - static const outlineVariantDark = Color(0xFF242630); + static const outlineVariantDark = Color(0xFF242329); static const outlineLight = Color(0xFF8E8E93); static const outlineVariantLight = Color(0xFFE4E4E7); } diff --git a/lib/core/theme/app_dimens.dart b/lib/core/theme/app_dimens.dart index ee86e8a..9bc0cb6 100644 --- a/lib/core/theme/app_dimens.dart +++ b/lib/core/theme/app_dimens.dart @@ -9,15 +9,17 @@ abstract final class AppDimens { static const double spaceXl = 24; static const double pageHorizontal = 16; - static const double cardRadius = 16; + static const double cardRadius = 12; static const double authCardRadius = 20; static const double cardPadding = 16; - static const double innerRadius = 12; + static const double innerRadius = 10; static const double innerPadding = 12; static const double pillRadius = 20; - static const double glassSmallRadius = 16; - static const double glassRadius = 24; - static const double glassCardRadius = 26; + static const double glassSmallRadius = 12; + static const double glassRadius = 14; + static const double glassCardRadius = 14; + static const double buttonRadius = 9; + static const double coverRadius = 10; static const double caption = 12; static const double emptyStateSubtitle = 12.5; @@ -27,17 +29,25 @@ abstract final class AppDimens { static const double sectionTitle = 16; static const double pageTitle = 20; static const double detailHeadline = 24; - static const double navLabel = 11; + static const double detailHeadlineSmall = 18; + static const double previewCardTitle = 13; + static const double previewSectionTitle = 14; + static const double chipFont = 10; + static const double navLabel = 9; static const double buttonHeight = 48; static const double primaryButtonHeight = 52; + static const double buttonCompact = 32; + static const double buttonSmall = 34; + static const double buttonMedium = 40; + static const double buttonLarge = 44; static const double tapTarget = 44; - static const double navHeight = 64; - static const double navIcon = 22; - static const double progressHeight = 4; - static const double progressRadius = 4; - static const double progressLineHeight = 1.5; + static const double navHeight = 54; + static const double navIcon = 17; + static const double progressHeight = 3; + static const double progressRadius = 2; + static const double progressLineHeight = 3; static const double searchHeight = 44; static const double navDot = 3; diff --git a/lib/core/widgets/app_bottom_nav.dart b/lib/core/widgets/app_bottom_nav.dart index ec7ce59..898d032 100644 --- a/lib/core/widgets/app_bottom_nav.dart +++ b/lib/core/widgets/app_bottom_nav.dart @@ -16,7 +16,6 @@ class AppBottomNavItem { required this.label, required this.onTap, this.selected = false, - this.indicatorKey, }); final Key tabKey; @@ -25,7 +24,6 @@ class AppBottomNavItem { final String label; final VoidCallback onTap; final bool selected; - final Key? indicatorKey; } /// Shared 3-tab navigation used by all root tabs and the player fallback. @@ -52,7 +50,9 @@ class AppBottomNav extends StatelessWidget { height: AppDimens.navHeight, decoration: BoxDecoration( color: backgroundColor.withValues(alpha: 0.95), - border: const Border(top: BorderSide(color: AppColors.border)), + border: const Border( + top: BorderSide(color: AppColors.outlineVariantDark), + ), ), child: Row( children: [for (final item in items) _NavDestination(item: item)], @@ -94,7 +94,7 @@ class _NavDestination extends StatelessWidget { : Colors.white.withValues(alpha: 0.45), ), ), - const SizedBox(height: 3), + const SizedBox(height: 2), Text( item.label, maxLines: 1, @@ -108,18 +108,6 @@ class _NavDestination extends StatelessWidget { letterSpacing: 0, ), ), - const SizedBox(height: 3), - AnimatedContainer( - key: item.indicatorKey, - duration: const Duration(milliseconds: 160), - curve: Curves.easeOut, - width: AppDimens.navDot, - height: AppDimens.navDot, - decoration: BoxDecoration( - color: item.selected ? AppColors.blue : Colors.transparent, - shape: BoxShape.circle, - ), - ), ], ), ), diff --git a/lib/core/widgets/glass_panel.dart b/lib/core/widgets/glass_panel.dart index 6e340d4..02d144c 100644 --- a/lib/core/widgets/glass_panel.dart +++ b/lib/core/widgets/glass_panel.dart @@ -13,7 +13,7 @@ class GlassPanel extends StatelessWidget { super.key, required this.child, this.radius = AppDimens.glassRadius, - this.blur = AppDimens.glassBlur, + this.blur = 10, this.padding, this.borderColor = AppColors.glassBorder, this.gradient, diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index ccf4bcf..671ee4f 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -15,6 +15,14 @@ "discoverTab": "Discover", "searchDiscover": "Search dramas, actors, genres...", "featured": "Featured", + "recommended": "Recommended", + "fruitShortsBrand": "FruitShorts", + "authLandingSubtitle": "Sign in to sync progress, favorites and coins", + "comingSoon": "Coming soon", + "authLegal": "By continuing, you agree to the Terms of Service and Privacy Policy.", + "authContinueGoogle": "Continue with Google", + "authPhoneSignIn": "Phone sign in", + "authEmailSignIn": "Email sign in", "suspense": "Suspense", "romance": "Romance", "actionGenre": "Action", @@ -31,6 +39,19 @@ "startWatching": "Start Watching", "showLess": "Less", "views": "views", + "episodeOfTotal": "Episode {current} / {total}", + "@episodeOfTotal": { + "placeholders": { + "current": {"type": "int"}, + "total": {"type": "int"} + } + }, + "minutesAgo": "{count} min ago", + "@minutesAgo": { + "placeholders": { + "count": {"type": "int"} + } + }, "artworkOnly": "Artwork", "artworkOnlyNotice": "This is a visual catalog item. No video is available yet.", "videoPending": "Video pending", @@ -52,7 +73,7 @@ }, "resume": "Resume", "history": "History", - "watchlist": "Watchlist", + "watchlist": "Favorites", "likedTab": "Liked", "all": "All", "inProgress": "In Progress", diff --git a/lib/l10n/app_id.arb b/lib/l10n/app_id.arb index 22a79f2..c75f7c3 100644 --- a/lib/l10n/app_id.arb +++ b/lib/l10n/app_id.arb @@ -12,9 +12,17 @@ "openQuickMenu": "Buka menu cepat", "quickAccess": "Akses cepat", "currentSeries": "Serial saat ini", - "discoverTab": "Jelajahi", + "discoverTab": "Temukan", "searchDiscover": "Cari drama, aktor, genre...", "featured": "Unggulan", + "recommended": "Rekomendasi", + "fruitShortsBrand": "FruitShorts", + "authLandingSubtitle": "Masuk untuk menyinkronkan progres, favorit, dan koin", + "comingSoon": "Segera hadir", + "authLegal": "Dengan melanjutkan, Anda menyetujui Ketentuan Layanan dan Kebijakan Privasi.", + "authContinueGoogle": "Lanjutkan dengan Google", + "authPhoneSignIn": "Masuk dengan ponsel", + "authEmailSignIn": "Masuk dengan email", "suspense": "Thriller", "romance": "Romantis", "actionGenre": "Aksi", @@ -31,6 +39,19 @@ "startWatching": "Mulai Menonton", "showLess": "Lebih sedikit", "views": "tayangan", + "episodeOfTotal": "Episode {current} / {total}", + "@episodeOfTotal": { + "placeholders": { + "current": {"type": "int"}, + "total": {"type": "int"} + } + }, + "minutesAgo": "{count} mnt lalu", + "@minutesAgo": { + "placeholders": { + "count": {"type": "int"} + } + }, "artworkOnly": "Gambar", "artworkOnlyNotice": "Ini hanya gambar katalog. Videonya belum tersedia.", "videoPending": "Video belum siap", diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index ee0214e..cf14047 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -190,6 +190,54 @@ abstract class AppLocalizations { /// **'Featured'** String get featured; + /// No description provided for @recommended. + /// + /// In en, this message translates to: + /// **'Recommended'** + String get recommended; + + /// No description provided for @fruitShortsBrand. + /// + /// In en, this message translates to: + /// **'FruitShorts'** + String get fruitShortsBrand; + + /// No description provided for @authLandingSubtitle. + /// + /// In en, this message translates to: + /// **'Sign in to sync progress, favorites and coins'** + String get authLandingSubtitle; + + /// No description provided for @comingSoon. + /// + /// In en, this message translates to: + /// **'Coming soon'** + String get comingSoon; + + /// No description provided for @authLegal. + /// + /// In en, this message translates to: + /// **'By continuing, you agree to the Terms of Service and Privacy Policy.'** + String get authLegal; + + /// No description provided for @authContinueGoogle. + /// + /// In en, this message translates to: + /// **'Continue with Google'** + String get authContinueGoogle; + + /// No description provided for @authPhoneSignIn. + /// + /// In en, this message translates to: + /// **'Phone sign in'** + String get authPhoneSignIn; + + /// No description provided for @authEmailSignIn. + /// + /// In en, this message translates to: + /// **'Email sign in'** + String get authEmailSignIn; + /// No description provided for @suspense. /// /// In en, this message translates to: @@ -286,6 +334,18 @@ abstract class AppLocalizations { /// **'views'** String get views; + /// No description provided for @episodeOfTotal. + /// + /// In en, this message translates to: + /// **'Episode {current} / {total}'** + String episodeOfTotal(int current, int total); + + /// No description provided for @minutesAgo. + /// + /// In en, this message translates to: + /// **'{count} min ago'** + String minutesAgo(int count); + /// No description provided for @artworkOnly. /// /// In en, this message translates to: @@ -373,7 +433,7 @@ abstract class AppLocalizations { /// No description provided for @watchlist. /// /// In en, this message translates to: - /// **'Watchlist'** + /// **'Favorites'** String get watchlist; /// No description provided for @likedTab. diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index d39d132..3a00090 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -54,6 +54,32 @@ class AppLocalizationsEn extends AppLocalizations { @override String get featured => 'Featured'; + @override + String get recommended => 'Recommended'; + + @override + String get fruitShortsBrand => 'FruitShorts'; + + @override + String get authLandingSubtitle => + 'Sign in to sync progress, favorites and coins'; + + @override + String get comingSoon => 'Coming soon'; + + @override + String get authLegal => + 'By continuing, you agree to the Terms of Service and Privacy Policy.'; + + @override + String get authContinueGoogle => 'Continue with Google'; + + @override + String get authPhoneSignIn => 'Phone sign in'; + + @override + String get authEmailSignIn => 'Email sign in'; + @override String get suspense => 'Suspense'; @@ -102,6 +128,16 @@ class AppLocalizationsEn extends AppLocalizations { @override String get views => 'views'; + @override + String episodeOfTotal(int current, int total) { + return 'Episode $current / $total'; + } + + @override + String minutesAgo(int count) { + return '$count min ago'; + } + @override String get artworkOnly => 'Artwork'; @@ -149,7 +185,7 @@ class AppLocalizationsEn extends AppLocalizations { String get history => 'History'; @override - String get watchlist => 'Watchlist'; + String get watchlist => 'Favorites'; @override String get likedTab => 'Liked'; diff --git a/lib/l10n/app_localizations_id.dart b/lib/l10n/app_localizations_id.dart index 06dd59f..5f2b421 100644 --- a/lib/l10n/app_localizations_id.dart +++ b/lib/l10n/app_localizations_id.dart @@ -46,7 +46,7 @@ class AppLocalizationsId extends AppLocalizations { String get currentSeries => 'Serial saat ini'; @override - String get discoverTab => 'Jelajahi'; + String get discoverTab => 'Temukan'; @override String get searchDiscover => 'Cari drama, aktor, genre...'; @@ -54,6 +54,32 @@ class AppLocalizationsId extends AppLocalizations { @override String get featured => 'Unggulan'; + @override + String get recommended => 'Rekomendasi'; + + @override + String get fruitShortsBrand => 'FruitShorts'; + + @override + String get authLandingSubtitle => + 'Masuk untuk menyinkronkan progres, favorit, dan koin'; + + @override + String get comingSoon => 'Segera hadir'; + + @override + String get authLegal => + 'Dengan melanjutkan, Anda menyetujui Ketentuan Layanan dan Kebijakan Privasi.'; + + @override + String get authContinueGoogle => 'Lanjutkan dengan Google'; + + @override + String get authPhoneSignIn => 'Masuk dengan ponsel'; + + @override + String get authEmailSignIn => 'Masuk dengan email'; + @override String get suspense => 'Thriller'; @@ -102,6 +128,16 @@ class AppLocalizationsId extends AppLocalizations { @override String get views => 'tayangan'; + @override + String episodeOfTotal(int current, int total) { + return 'Episode $current / $total'; + } + + @override + String minutesAgo(int count) { + return '$count mnt lalu'; + } + @override String get artworkOnly => 'Gambar'; diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart index 8453aca..b3fb2b3 100644 --- a/lib/l10n/app_localizations_zh.dart +++ b/lib/l10n/app_localizations_zh.dart @@ -45,7 +45,7 @@ class AppLocalizationsZh extends AppLocalizations { String get currentSeries => '当前短剧'; @override - String get discoverTab => '找剧'; + String get discoverTab => '发现'; @override String get searchDiscover => '搜索短剧、演员、类型…'; @@ -53,6 +53,30 @@ class AppLocalizationsZh extends AppLocalizations { @override String get featured => '精选'; + @override + String get recommended => '推荐'; + + @override + String get fruitShortsBrand => '水果短剧'; + + @override + String get authLandingSubtitle => '登录后同步进度、收藏与金币权益'; + + @override + String get comingSoon => '即将开放'; + + @override + String get authLegal => '继续即代表同意《用户协议》与《隐私政策》'; + + @override + String get authContinueGoogle => '使用 Google 继续'; + + @override + String get authPhoneSignIn => '手机号登录'; + + @override + String get authEmailSignIn => '邮箱登录'; + @override String get suspense => '悬疑'; @@ -101,6 +125,16 @@ class AppLocalizationsZh extends AppLocalizations { @override String get views => '次播放'; + @override + String episodeOfTotal(int current, int total) { + return '第 $current 集 / 共 $total 集'; + } + + @override + String minutesAgo(int count) { + return '$count 分钟前'; + } + @override String get artworkOnly => '海报展示'; @@ -146,7 +180,7 @@ class AppLocalizationsZh extends AppLocalizations { String get history => '历史'; @override - String get watchlist => '追剧'; + String get watchlist => '收藏'; @override String get likedTab => '喜欢'; diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 374f17a..0ddd3bb 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -12,9 +12,17 @@ "openQuickMenu": "打开快捷菜单", "quickAccess": "快捷入口", "currentSeries": "当前短剧", - "discoverTab": "找剧", + "discoverTab": "发现", "searchDiscover": "搜索短剧、演员、类型…", "featured": "精选", + "recommended": "推荐", + "fruitShortsBrand": "水果短剧", + "authLandingSubtitle": "登录后同步进度、收藏与金币权益", + "comingSoon": "即将开放", + "authLegal": "继续即代表同意《用户协议》与《隐私政策》", + "authContinueGoogle": "使用 Google 继续", + "authPhoneSignIn": "手机号登录", + "authEmailSignIn": "邮箱登录", "suspense": "悬疑", "romance": "都市爱情", "actionGenre": "动作", @@ -31,6 +39,19 @@ "startWatching": "开始观看", "showLess": "收起", "views": "次播放", + "episodeOfTotal": "第 {current} 集 / 共 {total} 集", + "@episodeOfTotal": { + "placeholders": { + "current": {"type": "int"}, + "total": {"type": "int"} + } + }, + "minutesAgo": "{count} 分钟前", + "@minutesAgo": { + "placeholders": { + "count": {"type": "int"} + } + }, "artworkOnly": "海报展示", "artworkOnlyNotice": "这张海报仅用于目录展示,暂时没有可播放视频。", "videoPending": "视频待提供", @@ -52,7 +73,7 @@ }, "resume": "继续播放", "history": "历史", - "watchlist": "追剧", + "watchlist": "收藏", "likedTab": "喜欢", "all": "全部", "inProgress": "未看完", diff --git a/lib/pages/home/home_view.dart b/lib/pages/home/home_view.dart index 069046b..2bdb917 100644 --- a/lib/pages/home/home_view.dart +++ b/lib/pages/home/home_view.dart @@ -245,11 +245,10 @@ class _HomeTabsState extends State<_HomeTabs> { items: [ AppBottomNavItem( tabKey: const Key('tab-home'), - icon: Icons.home_outlined, - selectedIcon: Icons.home_rounded, + icon: Icons.play_circle_outline_rounded, + selectedIcon: Icons.play_circle_fill_rounded, label: l10n.homeTab, selected: logic.tab == 0, - indicatorKey: const Key('tab-home-indicator'), onTap: () => _selectTab(context, 0), ), AppBottomNavItem( @@ -258,16 +257,14 @@ class _HomeTabsState extends State<_HomeTabs> { selectedIcon: Icons.explore_rounded, label: l10n.discoverTab, selected: logic.tab == 1, - indicatorKey: const Key('tab-discover-indicator'), onTap: () => _selectTab(context, 1), ), AppBottomNavItem( tabKey: const Key('tab-my'), - icon: Icons.person_outline_rounded, - selectedIcon: Icons.person_rounded, + icon: Icons.video_library_outlined, + selectedIcon: Icons.video_library_rounded, label: l10n.myTab, selected: logic.tab == 2, - indicatorKey: const Key('tab-my-indicator'), onTap: () => _selectTab(context, 2, myProfile: true), ), ], diff --git a/test/theme/app_dimens_test.dart b/test/theme/app_dimens_test.dart index 149b85e..f4402d4 100644 --- a/test/theme/app_dimens_test.dart +++ b/test/theme/app_dimens_test.dart @@ -6,18 +6,18 @@ import 'package:fruitshorts/core/theme/app_dimens.dart'; void main() { test('should_expose_the_k20_spacing_and_type_scale', () { expect(AppDimens.pageHorizontal, 16); - expect(AppDimens.cardRadius, 16); - expect(AppDimens.innerRadius, 12); + expect(AppDimens.cardRadius, 12); + expect(AppDimens.innerRadius, 10); expect(AppDimens.caption, 12); expect(AppDimens.body, 14); expect(AppDimens.listTitle, 15); expect(AppDimens.sectionTitle, 16); expect(AppDimens.pageTitle, 20); - expect(AppDimens.navLabel, 11); + expect(AppDimens.navLabel, 9); expect(AppDimens.buttonHeight, 48); expect(AppDimens.primaryButtonHeight, 52); expect(AppDimens.tapTarget, 44); - expect(AppDimens.navHeight, 64); - expect(AppDimens.progressHeight, 4); + expect(AppDimens.navHeight, 54); + expect(AppDimens.progressHeight, 3); }); } diff --git a/test/theme/app_theme_test.dart b/test/theme/app_theme_test.dart index ea9b91c..4a7eb85 100644 --- a/test/theme/app_theme_test.dart +++ b/test/theme/app_theme_test.dart @@ -21,8 +21,10 @@ void main() { expect(AppColors.secondary, const Color(0xFF9278EA)); expect(AppColors.gold, const Color(0xFFFBBF24)); expect(AppColors.ink, const Color(0xFF0B0C10)); - expect(AppColors.card, const Color(0xFF16181F)); - expect(AppColors.raised, const Color(0xFF1D1E28)); + expect(AppColors.card, const Color(0xFF1B1C21)); + expect(AppColors.cardAlt, const Color(0xFF1C1D21)); + expect(AppColors.raised, const Color(0xFF1C1D21)); + expect(AppColors.bar, const Color(0xFF0D0D11)); expect(AppColors.success, const Color(0xFF34D399)); expect(AppColors.onPrimary, const Color(0xFFFFFFFF)); expect(