feat(mine): 重做我的页会员卡与功能宫格布局

This commit is contained in:
Vicky authored and trae-solo-agent committed 2026-10-30 17:14:32 +08:00
1 parent e7399fa3fd
commit 53e8c924a6
12 files changed
+1969 -513

No files matched your search

+109 -27
View File
@@ -16,7 +16,7 @@ import 'package:fruitshorts/l10n/app_localizations.dart';
import 'package:fruitshorts/pages/auth/auth_view.dart';
import 'package:fruitshorts/pages/mine/about_page.dart';
import 'package:fruitshorts/pages/mine/mine_types.dart';
import 'package:fruitshorts/pages/mine/widgets/mine_menu.dart';
import 'package:fruitshorts/pages/mine/widgets/mine_action_grid.dart';
import 'package:fruitshorts/pages/store/vip_page.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';
@@ -78,11 +78,11 @@ void main() {
]);
expect(background.stops, const [0, 0.14, 0.3]);
// 快捷图标行:钱包 / 收藏 / 离线缓存 / 观看历史。
// 统一操作宫格第一排:钱包 / 收藏 / 离线缓存 / 观看历史。
const quickActions = [
(Key('mine-wallet'), Icons.wallet_outlined),
(Key('mine-favorites'), Icons.star_border_rounded),
(Key('mine-downloads'), Icons.download_outlined),
(Key('mine-wallet'), Icons.account_balance_wallet_outlined),
(Key('mine-favorites'), Icons.star_outline_rounded),
(Key('mine-downloads'), Icons.download_for_offline_outlined),
(Key('mine-history'), Icons.history_rounded),
];
for (final action in quickActions) {
@@ -92,44 +92,124 @@ void main() {
matching: find.byIcon(action.$2),
),
);
expect(icon.color, AppColors.textPrimary);
expect(icon.size, AppDimens.mineQuickActionIcon);
expect(icon.color, AppColors.mineGridIconAccent);
expect(icon.size, AppDimens.mineGridIcon);
expect(
find.descendant(
of: find.byKey(const Key('mine-menu')),
matching: find.byKey(action.$1),
),
findsOneWidget,
);
}
// 「更多服务」列表:联系客服 / 语言 / 设置 / 通知。
// 「更多服务」宫格:联系客服 / 语言 / 设置 / 通知。
const services = [
(Key('mine-help'), Icons.headset_outlined),
(Key('mine-language'), Icons.language_rounded),
(Key('mine-settings'), Icons.settings_outlined),
(Key('mine-notices'), Icons.notifications_none_rounded),
(
Key('mine-help'),
Key('mine-help-icon-surface'),
Icons.support_agent_outlined,
),
(
Key('mine-language'),
Key('mine-language-icon-surface'),
Icons.language_rounded,
),
(
Key('mine-settings'),
Key('mine-settings-icon-surface'),
Icons.settings_outlined,
),
(
Key('mine-notices'),
Key('mine-notices-icon-surface'),
Icons.notifications_none_rounded,
),
];
for (final service in services) {
expect(find.byKey(service.$1), findsOneWidget);
final icon = tester.widget<Icon>(
find.descendant(
of: find.byKey(service.$1),
matching: find.byIcon(service.$2),
matching: find.byIcon(service.$3),
),
);
expect(icon.color, AppColors.textPrimary);
expect(icon.size, AppDimens.mineServiceIcon);
expect(icon.color, AppColors.mineGridIconAccent);
expect(icon.size, AppDimens.mineGridIcon);
expect(
tester.getSize(find.byKey(service.$2)),
const Size.square(AppDimens.mineGridIconSurface),
);
}
expect(find.byKey(const Key('mine-menu')), findsOneWidget);
expect(tester.getTopLeft(find.byKey(const Key('mine-menu'))).dx, 14);
expect(
tester.getSize(find.byKey(const Key('mine-menu'))).height,
AppDimens.mineGridHeight,
);
final serviceLefts = services
.map((service) => tester.getTopLeft(find.byKey(service.$1)).dx)
.toList();
expect(serviceLefts, [...serviceLefts]..sort());
expect(serviceLefts.toSet(), hasLength(services.length));
expect(
tester.getTopLeft(find.byKey(const Key('mine-help'))).dy,
greaterThan(tester.getTopLeft(find.byKey(const Key('mine-wallet'))).dy),
);
final menu = tester.widget<DecoratedBox>(
find.byKey(const Key('mine-menu')),
);
final menuDecoration = menu.decoration as BoxDecoration;
expect(menuDecoration.color, AppColors.mineGridSurface);
expect(
menuDecoration.borderRadius,
BorderRadius.circular(AppDimens.mineGridRadius),
);
final vip = tester.widget<Container>(find.byKey(const Key('mine-vip')));
final vipDecoration = vip.decoration! as BoxDecoration;
expect((vipDecoration.gradient! as LinearGradient).colors, const [
AppColors.vipCardStart,
AppColors.vipCardMiddle,
AppColors.vipCardEnd,
]);
expect(vipDecoration.boxShadow, isNull);
expect(vipDecoration.border, isNotNull);
expect(vipDecoration.boxShadow, isNotEmpty);
// 卡片高度由内容撑开,上下内边距必须一致(边框各占 1pt)。
final vipRect = tester.getRect(find.byKey(const Key('mine-vip')));
final emblemRect = tester.getRect(find.byKey(const Key('mine-vip-emblem')));
final benefitsRect = tester.getRect(
find.byKey(const Key('mine-vip-benefits')),
);
final topInset = emblemRect.top - vipRect.top;
final bottomInset = vipRect.bottom - benefitsRect.bottom;
expect(topInset, closeTo(bottomInset, 0.01));
expect(topInset, greaterThanOrEqualTo(AppDimens.mineVipPadding));
expect(find.byKey(const Key('mine-vip-emblem')), findsOneWidget);
expect(
tester.getSize(find.byKey(const Key('mine-vip-open'))).height,
greaterThanOrEqualTo(AppDimens.tapTarget),
);
const benefitKeys = [
Key('mine-vip-benefit-0'),
Key('mine-vip-benefit-1'),
Key('mine-vip-benefit-2'),
Key('mine-vip-benefit-3'),
];
final benefitTops = benefitKeys
.map((key) => tester.getTopLeft(find.byKey(key)))
.toList();
expect(benefitTops.map((position) => position.dy).toSet(), hasLength(1));
expect(
benefitTops.map((position) => position.dx).toSet(),
hasLength(benefitKeys.length),
);
final order = [
tester.getTopLeft(find.byKey(const Key('mine-user-row'))).dy,
tester.getTopLeft(find.byKey(const Key('mine-vip'))).dy,
tester.getTopLeft(find.byKey(const Key('mine-wallet'))).dy,
tester.getTopLeft(find.byKey(const Key('mine-help'))).dy,
];
expect(order, [...order]..sort());
expect(tester.takeException(), isNull);
@@ -181,6 +261,7 @@ void main() {
expect(find.byType(MineAboutPage), findsOneWidget);
expect(find.byKey(const Key('mine-about-page')), findsOneWidget);
expect(find.text('Version 1.0.0'), findsOneWidget);
expect(find.text('Your mini business'), findsOneWidget);
expect(
find.text(
AppLocalizations.of(
@@ -245,16 +326,17 @@ void main() {
padding: const EdgeInsets.all(AppDimens.pageHorizontal),
child: Column(
children: [
MineQuickActions(
onWallet: () => opened.add(MineDestination.wallet),
onFavorites: () => favoritesOpened++,
onDownloads: () => opened.add(MineDestination.downloads),
onHistory: () => opened.add(MineDestination.history),
),
MineServiceList(
onOpen: opened.add,
onLanguage: () => languageOpened++,
languageName: 'English',
MineActionGrid(
primaryActions: MinePrimaryActions(
onWallet: () => opened.add(MineDestination.wallet),
onFavorites: () => favoritesOpened++,
onDownloads: () => opened.add(MineDestination.downloads),
onHistory: () => opened.add(MineDestination.history),
),
serviceActions: MineServiceActions(
onOpen: opened.add,
onLanguage: () => languageOpened++,
),
),
],
),