merge: 统一我的页深色视觉风格

This commit is contained in:
Zhou Weidong committed 2026-10-22 17:07:32 +08:00
commit 0ef2438100
7 files changed
+242 -82

No files matched your search

+34
View File
@@ -903,3 +903,37 @@ $ flutter test
解读:三列网格、分类顺序、动态入口隐藏、角标、卡片信息、分页大小、 解读:三列网格、分类顺序、动态入口隐藏、角标、卡片信息、分页大小、
下一页追加和卡片进入播放器均有测试覆盖。`home_view.dart` 为 478 行, 下一页追加和卡片进入播放器均有测试覆盖。`home_view.dart` 为 478 行,
其余新增组件均低于 500 行。`pubspec.lock` 未提交,未操作真机。 其余新增组件均低于 500 行。`pubspec.lock` 未提交,未操作真机。
## 2026-10-22 「我的」页视觉风格统一
- 分支:`feature/mine-visual-style`
- 基线:`develop` / `origin/develop` = `47bf645`
- 实现提交:`83129ac feat(mine): 统一我的页深色视觉风格`
### Token 与改动
- 复用 P11 的 `appBackground/contentCard/contentSurface/contentBorder`,
新增 `textPrimary/mineCardRadius/mineRowHeight/minePageTitle/mineSectionTitle`。
- 页面底色改为纯黑,移除「我的」页的旧深蓝渐变;页头白色标题 21/w700。
- 头像、三宫格、金币卡、会员卡、菜单统一为深灰 16 圆角、1px 极淡描边,
无阴影、无渐变、无发光。
- 主文字/昵称/金额使用 `#FFFFFF`,次级说明和 chevron 使用 `#8E8E93`。
- 三宫格等宽,入口图标 24 线性;菜单行高 52、标题 15,分割线只存在行间。
- 橙色仅保留在充值、加入 VIP 等主操作;头像与菜单图标不再使用粉色/橙色装饰。
- 区块顺序、全部入口、跳转与交互逻辑保持不变。
### 门禁命令与原始输出
```text
$ flutter analyze
Analyzing reelshort-bottom-nav...
No issues found!
```
```text
$ flutter test
00:56 +651: All tests passed!
```
解读:背景、卡片 token、圆角/描边、无渐变阴影、三宫格等宽、菜单 52 行高、
入口保留和收藏页跳转均有测试覆盖。`pubspec.lock` 未提交,未操作真机。
+1
View File
@@ -70,6 +70,7 @@ abstract final class AppColors {
static const contentBorder = Color(0x1AFFFFFF); static const contentBorder = Color(0x1AFFFFFF);
/// 次级文字与列表 chevron。 /// 次级文字与列表 chevron。
static const textPrimary = Color(0xFFFFFFFF);
static const textSecondary = Color(0xFF8E8E93); static const textSecondary = Color(0xFF8E8E93);
/// 原生开屏与冷启动过渡底色。 /// 原生开屏与冷启动过渡底色。
+6
View File
@@ -87,4 +87,10 @@ abstract final class AppDimens {
static const double homeSectionIndicator = 2; static const double homeSectionIndicator = 2;
static const double homeIconSlot = 44; static const double homeIconSlot = 44;
static const double homeHeaderIcon = 22; static const double homeHeaderIcon = 22;
/// 「我的」页中性视觉。
static const double mineCardRadius = 16;
static const double mineRowHeight = 52;
static const double minePageTitle = 21;
static const double mineSectionTitle = 15;
} }
+27 -18
View File
@@ -80,21 +80,25 @@ class _MyPageState extends State<MyPage> {
Widget _page(BuildContext context) { Widget _page(BuildContext context) {
final l10n = AppLocalizations.of(context)!; final l10n = AppLocalizations.of(context)!;
final content = SafeArea( final content = ColoredBox(
bottom: false, key: const Key('mine-page-surface'),
child: Column( color: AppColors.appBackground,
children: [ child: SafeArea(
_topBar(l10n), bottom: false,
Expanded(child: _body(context, l10n)), child: Column(
if (!embedded) _bottomNav(), children: [
], _topBar(l10n),
Expanded(child: _body(context, l10n)),
if (!embedded) _bottomNav(),
],
),
), ),
); );
return Theme( return Theme(
data: AppTheme.dark(), data: AppTheme.dark(),
child: embedded child: embedded
? content ? content
: Scaffold(backgroundColor: AppColors.ink, body: content), : Scaffold(backgroundColor: AppColors.appBackground, body: content),
); );
} }
@@ -111,9 +115,9 @@ class _MyPageState extends State<MyPage> {
l10n.myTab, l10n.myTab,
style: const TextStyle( style: const TextStyle(
fontFamily: 'Outfit', fontFamily: 'Outfit',
color: AppColors.onSurface, color: AppColors.textPrimary,
fontSize: AppDimens.pageTitle, fontSize: AppDimens.minePageTitle,
fontWeight: FontWeight.w800, fontWeight: FontWeight.w700,
), ),
), ),
), ),
@@ -280,8 +284,10 @@ class _LanguageSheet extends StatelessWidget {
]; ];
return Material( return Material(
key: const Key('mine-language-sheet'), key: const Key('mine-language-sheet'),
color: AppColors.card, color: AppColors.contentCard,
borderRadius: const BorderRadius.vertical(top: Radius.circular(20)), borderRadius: const BorderRadius.vertical(
top: Radius.circular(AppDimens.mineCardRadius),
),
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
child: SafeArea( child: SafeArea(
top: false, top: false,
@@ -297,9 +303,9 @@ class _LanguageSheet extends StatelessWidget {
child: Text( child: Text(
l10n.language, l10n.language,
style: const TextStyle( style: const TextStyle(
color: AppColors.onSurface, color: AppColors.textPrimary,
fontSize: 20, fontSize: 20,
fontWeight: FontWeight.w800, fontWeight: FontWeight.w700,
), ),
), ),
), ),
@@ -316,7 +322,10 @@ class _LanguageSheet extends StatelessWidget {
contentPadding: const EdgeInsets.symmetric(horizontal: 4), contentPadding: const EdgeInsets.symmetric(horizontal: 4),
title: Text(option.$3), title: Text(option.$3),
trailing: selected?.languageCode == option.$1.languageCode trailing: selected?.languageCode == option.$1.languageCode
? const Icon(Icons.check_rounded, color: AppColors.blue) ? const Icon(
Icons.check_rounded,
color: AppColors.textPrimary,
)
: null, : null,
onTap: () => Navigator.pop(context, option.$1), onTap: () => Navigator.pop(context, option.$1),
), ),
@@ -351,7 +360,7 @@ class _MinePlaceholderPage extends StatelessWidget {
child: Text( child: Text(
l10n.comingSoon, l10n.comingSoon,
style: const TextStyle( style: const TextStyle(
color: AppColors.muted, color: AppColors.textSecondary,
fontSize: AppDimens.body, fontSize: AppDimens.body,
), ),
), ),
+10 -11
View File
@@ -6,7 +6,6 @@ import 'package:provider/provider.dart';
import '../../../core/data/services/auth_controller.dart'; import '../../../core/data/services/auth_controller.dart';
import '../../../core/theme/app_colors.dart'; import '../../../core/theme/app_colors.dart';
import '../../../core/theme/app_dimens.dart'; import '../../../core/theme/app_dimens.dart';
import '../../../core/theme/app_theme.dart';
import '../../../core/widgets/remote_image.dart'; import '../../../core/widgets/remote_image.dart';
import '../../../l10n/app_localizations.dart'; import '../../../l10n/app_localizations.dart';
@@ -31,13 +30,13 @@ class MineHeader extends StatelessWidget {
return Container( return Container(
key: const Key('mine-user-row'), key: const Key('mine-user-row'),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.card, color: AppColors.contentCard,
borderRadius: BorderRadius.circular(AppDimens.cardRadius), borderRadius: BorderRadius.circular(AppDimens.mineCardRadius),
boxShadow: const [AppTheme.cardShadow], border: Border.all(color: AppColors.contentBorder),
), ),
child: Material( child: Material(
color: AppColors.transparent, color: AppColors.transparent,
borderRadius: BorderRadius.circular(AppDimens.cardRadius), borderRadius: BorderRadius.circular(AppDimens.mineCardRadius),
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
child: InkWell( child: InkWell(
onTap: onTap, onTap: onTap,
@@ -50,7 +49,7 @@ class MineHeader extends StatelessWidget {
Expanded(child: _labels(name, subtitle)), Expanded(child: _labels(name, subtitle)),
const Icon( const Icon(
Icons.chevron_right_rounded, Icons.chevron_right_rounded,
color: AppColors.subtle, color: AppColors.textSecondary,
size: 20, size: 20,
), ),
], ],
@@ -67,14 +66,14 @@ class MineHeader extends StatelessWidget {
width: 64, width: 64,
height: 64, height: 64,
decoration: const BoxDecoration( decoration: const BoxDecoration(
color: AppColors.badgePink, color: AppColors.contentSurface,
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
child: path.isEmpty child: path.isEmpty
? const Icon( ? const Icon(
Icons.person_rounded, Icons.person_rounded,
color: AppColors.badgePinkText, color: AppColors.textPrimary,
size: 30, size: 30,
) )
: RemoteImage(path, fit: BoxFit.cover), : RemoteImage(path, fit: BoxFit.cover),
@@ -91,9 +90,9 @@ class MineHeader extends StatelessWidget {
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: const TextStyle( style: const TextStyle(
fontFamily: 'Outfit', fontFamily: 'Outfit',
color: AppColors.onSurface, color: AppColors.textPrimary,
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.w800, fontWeight: FontWeight.w700,
), ),
), ),
const SizedBox(height: AppDimens.spaceXs), const SizedBox(height: AppDimens.spaceXs),
@@ -102,7 +101,7 @@ class MineHeader extends StatelessWidget {
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: const TextStyle( style: const TextStyle(
color: AppColors.muted, color: AppColors.textSecondary,
fontSize: AppDimens.caption, fontSize: AppDimens.caption,
), ),
), ),
+60 -53
View File
@@ -4,7 +4,6 @@ import 'package:flutter/material.dart';
import '../../../core/theme/app_colors.dart'; import '../../../core/theme/app_colors.dart';
import '../../../core/theme/app_dimens.dart'; import '../../../core/theme/app_dimens.dart';
import '../../../core/theme/app_theme.dart';
import '../../../l10n/app_localizations.dart'; import '../../../l10n/app_localizations.dart';
import '../mine_types.dart'; import '../mine_types.dart';
@@ -22,12 +21,12 @@ class MineShortcutGrid extends StatelessWidget {
Expanded( Expanded(
child: _ShortcutTile( child: _ShortcutTile(
key: const Key('mine-watchlist'), key: const Key('mine-watchlist'),
icon: Icons.bookmark_rounded, icon: Icons.bookmark_border_rounded,
label: l10n.watchlist, label: l10n.watchlist,
onTap: () => onOpen(MineDestination.watchlist), onTap: () => onOpen(MineDestination.watchlist),
), ),
), ),
const SizedBox(width: 10), const SizedBox(width: AppDimens.homeGridGap),
Expanded( Expanded(
child: _ShortcutTile( child: _ShortcutTile(
key: const Key('mine-history'), key: const Key('mine-history'),
@@ -36,11 +35,11 @@ class MineShortcutGrid extends StatelessWidget {
onTap: () => onOpen(MineDestination.history), onTap: () => onOpen(MineDestination.history),
), ),
), ),
const SizedBox(width: 10), const SizedBox(width: AppDimens.homeGridGap),
Expanded( Expanded(
child: _ShortcutTile( child: _ShortcutTile(
key: const Key('mine-downloads'), key: const Key('mine-downloads'),
icon: Icons.download_rounded, icon: Icons.download_outlined,
label: l10n.downloads, label: l10n.downloads,
onTap: () => onOpen(MineDestination.downloads), onTap: () => onOpen(MineDestination.downloads),
), ),
@@ -66,13 +65,13 @@ class _ShortcutTile extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.card, color: AppColors.contentCard,
borderRadius: BorderRadius.circular(AppDimens.cardRadius), borderRadius: BorderRadius.circular(AppDimens.mineCardRadius),
boxShadow: const [AppTheme.cardShadow], border: Border.all(color: AppColors.contentBorder),
), ),
child: Material( child: Material(
color: AppColors.transparent, color: AppColors.transparent,
borderRadius: BorderRadius.circular(AppDimens.cardRadius), borderRadius: BorderRadius.circular(AppDimens.mineCardRadius),
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
child: InkWell( child: InkWell(
onTap: onTap, onTap: onTap,
@@ -81,16 +80,16 @@ class _ShortcutTile extends StatelessWidget {
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Icon(icon, color: AppColors.blue, size: 24), Icon(icon, color: AppColors.textPrimary, size: 24),
const SizedBox(height: AppDimens.spaceSm), const SizedBox(height: AppDimens.spaceSm),
Text( Text(
label, label,
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: const TextStyle( style: const TextStyle(
color: AppColors.onSurface, color: AppColors.textPrimary,
fontSize: AppDimens.caption, fontSize: AppDimens.caption,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w500,
), ),
), ),
], ],
@@ -122,20 +121,16 @@ class MineWalletCard extends StatelessWidget {
key: const Key('mine-wallet'), key: const Key('mine-wallet'),
padding: const EdgeInsets.all(AppDimens.cardPadding), padding: const EdgeInsets.all(AppDimens.cardPadding),
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: const LinearGradient( color: AppColors.contentCard,
begin: Alignment.topLeft, borderRadius: BorderRadius.circular(AppDimens.mineCardRadius),
end: Alignment.bottomRight, border: Border.all(color: AppColors.contentBorder),
colors: [AppColors.goldCard, AppColors.card],
),
borderRadius: BorderRadius.circular(AppDimens.cardRadius),
boxShadow: const [AppTheme.cardShadow],
), ),
child: Row( child: Row(
children: [ children: [
const Icon( const Icon(
Icons.monetization_on_rounded, Icons.toll_outlined,
color: AppColors.gold, color: AppColors.textSecondary,
size: 30, size: 28,
), ),
const SizedBox(width: AppDimens.spaceMd), const SizedBox(width: AppDimens.spaceMd),
Expanded( Expanded(
@@ -147,7 +142,7 @@ class MineWalletCard extends StatelessWidget {
Text( Text(
l10n.coinBalance, l10n.coinBalance,
style: const TextStyle( style: const TextStyle(
color: AppColors.muted, color: AppColors.textSecondary,
fontSize: AppDimens.caption, fontSize: AppDimens.caption,
), ),
), ),
@@ -155,9 +150,9 @@ class MineWalletCard extends StatelessWidget {
Text( Text(
balance, balance,
style: const TextStyle( style: const TextStyle(
color: AppColors.onSurface, color: AppColors.textPrimary,
fontSize: AppDimens.pageTitle, fontSize: AppDimens.pageTitle,
fontWeight: FontWeight.w800, fontWeight: FontWeight.w700,
), ),
), ),
], ],
@@ -171,7 +166,9 @@ class MineWalletCard extends StatelessWidget {
backgroundColor: AppColors.blue, backgroundColor: AppColors.blue,
foregroundColor: AppColors.onPrimary, foregroundColor: AppColors.onPrimary,
minimumSize: const Size(64, AppDimens.buttonCompact), minimumSize: const Size(64, AppDimens.buttonCompact),
shape: const StadiumBorder(), shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppDimens.buttonRadius),
),
), ),
child: Text(l10n.topUpWallet), child: Text(l10n.topUpWallet),
), ),
@@ -193,13 +190,13 @@ class MineVipCard extends StatelessWidget {
return Container( return Container(
key: const Key('mine-vip'), key: const Key('mine-vip'),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.card, color: AppColors.contentCard,
borderRadius: BorderRadius.circular(AppDimens.cardRadius), borderRadius: BorderRadius.circular(AppDimens.mineCardRadius),
boxShadow: const [AppTheme.cardShadow], border: Border.all(color: AppColors.contentBorder),
), ),
child: Material( child: Material(
color: AppColors.transparent, color: AppColors.transparent,
borderRadius: BorderRadius.circular(AppDimens.cardRadius), borderRadius: BorderRadius.circular(AppDimens.mineCardRadius),
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
child: InkWell( child: InkWell(
onTap: onOpen, onTap: onOpen,
@@ -211,12 +208,12 @@ class MineVipCard extends StatelessWidget {
width: AppDimens.buttonLarge, width: AppDimens.buttonLarge,
height: AppDimens.buttonLarge, height: AppDimens.buttonLarge,
decoration: const BoxDecoration( decoration: const BoxDecoration(
color: AppColors.badgePink, color: AppColors.contentSurface,
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
child: const Icon( child: const Icon(
Icons.workspace_premium_rounded, Icons.workspace_premium_rounded,
color: AppColors.badgePinkText, color: AppColors.textPrimary,
size: 22, size: 22,
), ),
), ),
@@ -228,9 +225,9 @@ class MineVipCard extends StatelessWidget {
Text( Text(
l10n.vipMembership, l10n.vipMembership,
style: const TextStyle( style: const TextStyle(
color: AppColors.onSurface, color: AppColors.textPrimary,
fontSize: AppDimens.listTitle, fontSize: AppDimens.mineSectionTitle,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w600,
), ),
), ),
const SizedBox(height: 3), const SizedBox(height: 3),
@@ -239,7 +236,7 @@ class MineVipCard extends StatelessWidget {
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: const TextStyle( style: const TextStyle(
color: AppColors.muted, color: AppColors.textSecondary,
fontSize: AppDimens.caption, fontSize: AppDimens.caption,
), ),
), ),
@@ -250,7 +247,7 @@ class MineVipCard extends StatelessWidget {
l10n.joinVip, l10n.joinVip,
style: const TextStyle( style: const TextStyle(
color: AppColors.blue, color: AppColors.blue,
fontSize: AppDimens.caption, fontSize: AppDimens.body,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
), ),
), ),
@@ -282,13 +279,13 @@ class MineMenuList extends StatelessWidget {
return Container( return Container(
key: const Key('mine-menu'), key: const Key('mine-menu'),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.card, color: AppColors.contentCard,
borderRadius: BorderRadius.circular(AppDimens.cardRadius), borderRadius: BorderRadius.circular(AppDimens.mineCardRadius),
boxShadow: const [AppTheme.cardShadow], border: Border.all(color: AppColors.contentBorder),
), ),
child: Material( child: Material(
color: AppColors.transparent, color: AppColors.transparent,
borderRadius: BorderRadius.circular(AppDimens.cardRadius), borderRadius: BorderRadius.circular(AppDimens.mineCardRadius),
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
child: Column( child: Column(
children: [ children: [
@@ -338,18 +335,19 @@ class MineMenuList extends StatelessWidget {
final l10n = AppLocalizations.of(context)!; final l10n = AppLocalizations.of(context)!;
return ListTile( return ListTile(
key: const Key('mine-language'), key: const Key('mine-language'),
minTileHeight: 48, minTileHeight: AppDimens.mineRowHeight,
contentPadding: const EdgeInsets.symmetric(horizontal: 14), contentPadding: const EdgeInsets.symmetric(horizontal: 14),
leading: const Icon( leading: const Icon(
Icons.language_rounded, Icons.language_rounded,
color: AppColors.blue, color: AppColors.textSecondary,
size: 20, size: 20,
), ),
title: Text( title: Text(
l10n.language, l10n.language,
style: const TextStyle( style: const TextStyle(
color: AppColors.onSurface, color: AppColors.textPrimary,
fontSize: AppDimens.body, fontSize: AppDimens.listTitle,
fontWeight: FontWeight.w500,
), ),
), ),
trailing: Row( trailing: Row(
@@ -357,12 +355,15 @@ class MineMenuList extends StatelessWidget {
children: [ children: [
Text( Text(
languageLabel, languageLabel,
style: const TextStyle(color: AppColors.muted, fontSize: 11), style: const TextStyle(
color: AppColors.textSecondary,
fontSize: AppDimens.caption,
),
), ),
const SizedBox(width: 4), const SizedBox(width: 4),
const Icon( const Icon(
Icons.chevron_right_rounded, Icons.chevron_right_rounded,
color: AppColors.subtle, color: AppColors.textSecondary,
size: 18, size: 18,
), ),
], ],
@@ -379,19 +380,20 @@ class MineMenuList extends StatelessWidget {
}) { }) {
return ListTile( return ListTile(
key: key, key: key,
minTileHeight: 48, minTileHeight: AppDimens.mineRowHeight,
contentPadding: const EdgeInsets.symmetric(horizontal: 14), contentPadding: const EdgeInsets.symmetric(horizontal: 14),
leading: Icon(icon, color: AppColors.blue, size: 20), leading: Icon(icon, color: AppColors.textSecondary, size: 20),
title: Text( title: Text(
title, title,
style: const TextStyle( style: const TextStyle(
color: AppColors.onSurface, color: AppColors.textPrimary,
fontSize: AppDimens.body, fontSize: AppDimens.listTitle,
fontWeight: FontWeight.w500,
), ),
), ),
trailing: const Icon( trailing: const Icon(
Icons.chevron_right_rounded, Icons.chevron_right_rounded,
color: AppColors.subtle, color: AppColors.textSecondary,
size: 18, size: 18,
), ),
onTap: () => onOpen(destination), onTap: () => onOpen(destination),
@@ -399,6 +401,11 @@ class MineMenuList extends StatelessWidget {
} }
Widget _divider() { Widget _divider() {
return const Divider(height: 1, indent: 14, endIndent: 14); return const Divider(
height: 1,
indent: 14,
endIndent: 14,
color: AppColors.contentSurface,
);
} }
} }
+104
View File
@@ -6,6 +6,8 @@ import 'package:fruitshorts/app.dart';
import 'package:fruitshorts/core/data/repositories/favorite_repository.dart'; import 'package:fruitshorts/core/data/repositories/favorite_repository.dart';
import 'package:fruitshorts/core/data/repositories/playback_repository.dart'; import 'package:fruitshorts/core/data/repositories/playback_repository.dart';
import 'package:fruitshorts/core/data/repositories/watch_history_repository.dart'; import 'package:fruitshorts/core/data/repositories/watch_history_repository.dart';
import 'package:fruitshorts/core/theme/app_colors.dart';
import 'package:fruitshorts/core/theme/app_dimens.dart';
import 'package:shared_preferences_platform_interface/in_memory_shared_preferences_async.dart'; import 'package:shared_preferences_platform_interface/in_memory_shared_preferences_async.dart';
import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart';
@@ -69,6 +71,108 @@ void main() {
expect(find.byKey(const Key('mine-library-tab-0')), findsNothing); expect(find.byKey(const Key('mine-library-tab-0')), findsNothing);
expect(find.byKey(const Key('mine-library-tab-1')), findsNothing); expect(find.byKey(const Key('mine-library-tab-1')), findsNothing);
expect(find.byKey(const Key('mine-library-tab-2')), findsNothing); expect(find.byKey(const Key('mine-library-tab-2')), findsNothing);
final surface = tester.widget<ColoredBox>(
find.byKey(const Key('mine-page-surface')),
);
expect(surface.color, AppColors.appBackground);
final title = tester.widget<Text>(
find.descendant(
of: find.byKey(const Key('mine-page-surface')),
matching: find.text('My'),
),
);
expect(title.style?.color, AppColors.textPrimary);
expect(title.style?.fontSize, AppDimens.minePageTitle);
expect(title.style?.fontWeight, FontWeight.w700);
for (final key in const [
Key('mine-user-row'),
Key('mine-wallet'),
Key('mine-vip'),
Key('mine-menu'),
]) {
final container = tester.widget<Container>(find.byKey(key));
final decoration = container.decoration! as BoxDecoration;
expect(decoration.color, AppColors.contentCard);
expect(
decoration.borderRadius,
BorderRadius.circular(AppDimens.mineCardRadius),
);
expect(decoration.gradient, isNull);
expect(decoration.boxShadow, isNull);
expect((decoration.border! as Border).top.color, AppColors.contentBorder);
}
final shortcutIcon = tester.widget<Icon>(
find.descendant(
of: find.byKey(const Key('mine-watchlist')),
matching: find.byType(Icon),
),
);
expect(shortcutIcon.size, 24);
expect(shortcutIcon.color, AppColors.textPrimary);
final shortcutKeys = [
const Key('mine-watchlist'),
const Key('mine-history'),
const Key('mine-downloads'),
];
final shortcutWidths = shortcutKeys
.map((key) => tester.getSize(find.byKey(key)).width)
.toList();
expect(shortcutWidths.toSet(), hasLength(1));
final shortcutCard = tester.widget<Container>(
find
.descendant(
of: find.byKey(const Key('mine-watchlist')),
matching: find.byType(Container),
)
.first,
);
final shortcutDecoration = shortcutCard.decoration! as BoxDecoration;
expect(shortcutDecoration.color, AppColors.contentCard);
expect(
shortcutDecoration.borderRadius,
BorderRadius.circular(AppDimens.mineCardRadius),
);
expect(shortcutDecoration.boxShadow, isNull);
final menuRows = find.descendant(
of: find.byKey(const Key('mine-menu')),
matching: find.byType(ListTile),
);
expect(menuRows, findsNWidgets(6));
for (final row in tester.widgetList<ListTile>(menuRows)) {
expect(row.minTileHeight, AppDimens.mineRowHeight);
}
final settingsRow = tester.widget<ListTile>(
find.byKey(const Key('mine-settings')),
);
final settingsTitle = settingsRow.title! as Text;
expect(settingsTitle.style?.color, AppColors.textPrimary);
expect(settingsTitle.style?.fontSize, AppDimens.listTitle);
expect((settingsRow.trailing! as Icon).color, AppColors.textSecondary);
final recharge = tester.widget<TextButton>(
find.byKey(const Key('mine-wallet-recharge')),
);
expect(recharge.style?.backgroundColor?.resolve(const {}), AppColors.blue);
expect(
tester.widget<Text>(find.text('Join VIP')).style?.color,
AppColors.blue,
);
final dividers = tester.widgetList<Divider>(
find.descendant(
of: find.byKey(const Key('mine-menu')),
matching: find.byType(Divider),
),
);
expect(dividers, hasLength(5));
expect(
dividers.every((divider) => divider.color == AppColors.contentSurface),
isTrue,
);
expect(tester.takeException(), isNull); expect(tester.takeException(), isNull);
await tester.tap(find.byKey(const Key('mine-watchlist')));
await tester.pumpAndSettle();
expect(find.byKey(const Key('favorites-page')), findsOneWidget);
}); });
} }