style: 增强我的页渐变与图标配色
This commit is contained in:
1 parent
647f6afd77
commit
a81bec4d47
6 files changed
+116
-49
No files matched your search
@@ -1260,3 +1260,5 @@ $ flutter test
|
|||||||
- 页面背景改为暖黑三段纵向渐变,用户区与 VIP 卡布局保持不变。
|
- 页面背景改为暖黑三段纵向渐变,用户区与 VIP 卡布局保持不变。
|
||||||
- 行式入口替换为 4 列图标网格,7 个原入口按顺序保留,第 8 格为空占位。
|
- 行式入口替换为 4 列图标网格,7 个原入口按顺序保留,第 8 格为空占位。
|
||||||
- 网格使用 14 边距、半透明深灰底、圆角 12 和仅内部 1px 分隔线;`flutter analyze`:No issues found。
|
- 网格使用 14 边距、半透明深灰底、圆角 12 和仅内部 1px 分隔线;`flutter analyze`:No issues found。
|
||||||
|
- 渐变增强为 `#3A1B0E`/`#1A0C07`/黑色并叠加顶部品牌橙径向光晕,同时移除页面级「我的」标题栏。
|
||||||
|
- 7 个宫格图标分别使用金币金、天蓝、薄荷绿、粉红、紫色、青色和冷灰;`flutter analyze`:No issues found。
|
||||||
@@ -84,13 +84,21 @@ abstract final class AppColors {
|
|||||||
/// 「我的」页卡片与描边。
|
/// 「我的」页卡片与描边。
|
||||||
static const mineCard = Color(0xFF16161A);
|
static const mineCard = Color(0xFF16161A);
|
||||||
static const mineOutline = Color(0xFF4A4A4A);
|
static const mineOutline = Color(0xFF4A4A4A);
|
||||||
static const mineGradientTop = Color(0xFF1A0F0A);
|
static const mineGradientTop = Color(0xFF3A1B0E);
|
||||||
static const mineGradientMiddle = Color(0xFF0D0908);
|
static const mineGradientMiddle = Color(0xFF1A0C07);
|
||||||
static const mineGradientBottom = Color(0xFF000000);
|
static const mineGradientBottom = Color(0xFF000000);
|
||||||
|
static const mineGradientGlow = Color(0x33FF6B2C);
|
||||||
static const mineGridSurface = Color(0x99101010);
|
static const mineGridSurface = Color(0x99101010);
|
||||||
static const mineGridDivider = Color(0xFF2A2A2A);
|
static const mineGridDivider = Color(0xFF2A2A2A);
|
||||||
static const mineGridIconSurface = Color(0xFF1C1C1E);
|
static const mineGridIconSurface = Color(0xFF1C1C1E);
|
||||||
static const mineGridLabel = Color(0xFFE6E6EB);
|
static const mineGridLabel = Color(0xFFE6E6EB);
|
||||||
|
static const mineIconWallet = Color(0xFFF5C451);
|
||||||
|
static const mineIconDownloads = Color(0xFF4DA3FF);
|
||||||
|
static const mineIconHistory = Color(0xFF4ADE80);
|
||||||
|
static const mineIconPreferences = Color(0xFFFF6B9A);
|
||||||
|
static const mineIconSupport = Color(0xFFA78BFA);
|
||||||
|
static const mineIconLanguage = Color(0xFF22D3EE);
|
||||||
|
static const mineIconSettings = Color(0xFFA1A1AA);
|
||||||
|
|
||||||
/// 次级文字与列表 chevron。
|
/// 次级文字与列表 chevron。
|
||||||
static const textPrimary = Color(0xFFFFFFFF);
|
static const textPrimary = Color(0xFFFFFFFF);
|
||||||
|
|||||||
@@ -179,7 +179,6 @@ abstract final class AppDimens {
|
|||||||
|
|
||||||
/// 「我的」页中性视觉。
|
/// 「我的」页中性视觉。
|
||||||
static const double mineCardRadius = 16;
|
static const double mineCardRadius = 16;
|
||||||
static const double minePageTitle = 17;
|
|
||||||
static const double mineSectionTitle = 15;
|
static const double mineSectionTitle = 15;
|
||||||
static const double mineAvatar = 56;
|
static const double mineAvatar = 56;
|
||||||
static const double mineUserName = 20;
|
static const double mineUserName = 20;
|
||||||
|
|||||||
@@ -80,7 +80,6 @@ class _MyPageState extends State<MyPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _page(BuildContext context) {
|
Widget _page(BuildContext context) {
|
||||||
final l10n = AppLocalizations.of(context)!;
|
|
||||||
final content = DecoratedBox(
|
final content = DecoratedBox(
|
||||||
key: const Key('mine-page-surface'),
|
key: const Key('mine-page-surface'),
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
@@ -92,18 +91,32 @@ class _MyPageState extends State<MyPage> {
|
|||||||
AppColors.mineGradientMiddle,
|
AppColors.mineGradientMiddle,
|
||||||
AppColors.mineGradientBottom,
|
AppColors.mineGradientBottom,
|
||||||
],
|
],
|
||||||
stops: [0, 0.48, 1],
|
stops: [0, 0.4, 0.75],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: SafeArea(
|
child: Stack(
|
||||||
bottom: false,
|
fit: StackFit.expand,
|
||||||
child: Column(
|
children: [
|
||||||
children: [
|
const DecoratedBox(
|
||||||
_topBar(l10n),
|
key: Key('mine-page-glow'),
|
||||||
Expanded(child: _body(context)),
|
decoration: BoxDecoration(
|
||||||
if (!embedded) _bottomNav(),
|
gradient: RadialGradient(
|
||||||
],
|
center: Alignment(0, -0.95),
|
||||||
),
|
radius: 0.9,
|
||||||
|
colors: [AppColors.mineGradientGlow, AppColors.transparent],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SafeArea(
|
||||||
|
bottom: false,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Expanded(child: _body(context)),
|
||||||
|
if (!embedded) _bottomNav(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
@@ -117,29 +130,6 @@ class _MyPageState extends State<MyPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _topBar(AppLocalizations l10n) {
|
|
||||||
return SizedBox(
|
|
||||||
height: AppDimens.contentHeaderHeight,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: AppDimens.pageHorizontal,
|
|
||||||
),
|
|
||||||
child: Align(
|
|
||||||
alignment: Alignment.centerLeft,
|
|
||||||
child: Text(
|
|
||||||
l10n.myTab,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontFamily: 'Outfit',
|
|
||||||
color: AppColors.textPrimary,
|
|
||||||
fontSize: AppDimens.minePageTitle,
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _body(BuildContext context) {
|
Widget _body(BuildContext context) {
|
||||||
return ListView(
|
return ListView(
|
||||||
key: const Key('mine-page-scroll'),
|
key: const Key('mine-page-scroll'),
|
||||||
|
|||||||
@@ -153,6 +153,7 @@ class MineMenuList extends StatelessWidget {
|
|||||||
index: 0,
|
index: 0,
|
||||||
actionKey: const Key('mine-wallet'),
|
actionKey: const Key('mine-wallet'),
|
||||||
icon: Icons.account_balance_wallet_outlined,
|
icon: Icons.account_balance_wallet_outlined,
|
||||||
|
iconColor: AppColors.mineIconWallet,
|
||||||
label: l10n.wallet,
|
label: l10n.wallet,
|
||||||
onTap: () => onOpen(MineDestination.wallet),
|
onTap: () => onOpen(MineDestination.wallet),
|
||||||
),
|
),
|
||||||
@@ -160,6 +161,7 @@ class MineMenuList extends StatelessWidget {
|
|||||||
index: 1,
|
index: 1,
|
||||||
actionKey: const Key('mine-downloads'),
|
actionKey: const Key('mine-downloads'),
|
||||||
icon: Icons.download_outlined,
|
icon: Icons.download_outlined,
|
||||||
|
iconColor: AppColors.mineIconDownloads,
|
||||||
label: l10n.offlineDownloads,
|
label: l10n.offlineDownloads,
|
||||||
onTap: () => onOpen(MineDestination.downloads),
|
onTap: () => onOpen(MineDestination.downloads),
|
||||||
),
|
),
|
||||||
@@ -167,6 +169,7 @@ class MineMenuList extends StatelessWidget {
|
|||||||
index: 2,
|
index: 2,
|
||||||
actionKey: const Key('mine-history'),
|
actionKey: const Key('mine-history'),
|
||||||
icon: Icons.history_rounded,
|
icon: Icons.history_rounded,
|
||||||
|
iconColor: AppColors.mineIconHistory,
|
||||||
label: l10n.watchHistory,
|
label: l10n.watchHistory,
|
||||||
onTap: () => onOpen(MineDestination.history),
|
onTap: () => onOpen(MineDestination.history),
|
||||||
),
|
),
|
||||||
@@ -174,6 +177,7 @@ class MineMenuList extends StatelessWidget {
|
|||||||
index: 3,
|
index: 3,
|
||||||
actionKey: const Key('mine-watch-preferences'),
|
actionKey: const Key('mine-watch-preferences'),
|
||||||
icon: Icons.tune_rounded,
|
icon: Icons.tune_rounded,
|
||||||
|
iconColor: AppColors.mineIconPreferences,
|
||||||
label: l10n.watchPreferences,
|
label: l10n.watchPreferences,
|
||||||
onTap: () => onOpen(MineDestination.preferences),
|
onTap: () => onOpen(MineDestination.preferences),
|
||||||
),
|
),
|
||||||
@@ -185,6 +189,7 @@ class MineMenuList extends StatelessWidget {
|
|||||||
index: 4,
|
index: 4,
|
||||||
actionKey: const Key('mine-help'),
|
actionKey: const Key('mine-help'),
|
||||||
icon: Icons.support_agent_rounded,
|
icon: Icons.support_agent_rounded,
|
||||||
|
iconColor: AppColors.mineIconSupport,
|
||||||
label: l10n.contactSupport,
|
label: l10n.contactSupport,
|
||||||
onTap: () => onOpen(MineDestination.help),
|
onTap: () => onOpen(MineDestination.help),
|
||||||
),
|
),
|
||||||
@@ -192,6 +197,7 @@ class MineMenuList extends StatelessWidget {
|
|||||||
index: 5,
|
index: 5,
|
||||||
actionKey: const Key('mine-language'),
|
actionKey: const Key('mine-language'),
|
||||||
icon: Icons.language_rounded,
|
icon: Icons.language_rounded,
|
||||||
|
iconColor: AppColors.mineIconLanguage,
|
||||||
label: l10n.language,
|
label: l10n.language,
|
||||||
onTap: onLanguage,
|
onTap: onLanguage,
|
||||||
),
|
),
|
||||||
@@ -199,6 +205,7 @@ class MineMenuList extends StatelessWidget {
|
|||||||
index: 6,
|
index: 6,
|
||||||
actionKey: const Key('mine-settings'),
|
actionKey: const Key('mine-settings'),
|
||||||
icon: Icons.settings_outlined,
|
icon: Icons.settings_outlined,
|
||||||
|
iconColor: AppColors.mineIconSettings,
|
||||||
label: l10n.settings,
|
label: l10n.settings,
|
||||||
onTap: () => onOpen(MineDestination.settings),
|
onTap: () => onOpen(MineDestination.settings),
|
||||||
),
|
),
|
||||||
@@ -215,6 +222,7 @@ class MineMenuList extends StatelessWidget {
|
|||||||
required int index,
|
required int index,
|
||||||
Key? actionKey,
|
Key? actionKey,
|
||||||
IconData? icon,
|
IconData? icon,
|
||||||
|
Color? iconColor,
|
||||||
String? label,
|
String? label,
|
||||||
VoidCallback? onTap,
|
VoidCallback? onTap,
|
||||||
}) {
|
}) {
|
||||||
@@ -237,10 +245,16 @@ class MineMenuList extends StatelessWidget {
|
|||||||
color: AppColors.mineGridIconSurface,
|
color: AppColors.mineGridIconSurface,
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: Icon(
|
child: DecoratedBox(
|
||||||
icon,
|
decoration: BoxDecoration(
|
||||||
color: AppColors.primary,
|
color: iconColor?.withValues(alpha: 0.08),
|
||||||
size: AppDimens.mineGridIcon,
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
icon,
|
||||||
|
color: iconColor,
|
||||||
|
size: AppDimens.mineGridIcon,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: AppDimens.spaceXs + 2),
|
const SizedBox(height: AppDimens.spaceXs + 2),
|
||||||
|
|||||||
@@ -41,14 +41,6 @@ void main() {
|
|||||||
expect(find.byKey(const Key('mine-vip')), findsOneWidget);
|
expect(find.byKey(const Key('mine-vip')), findsOneWidget);
|
||||||
expect(find.byKey(const Key('mine-vip-open')), findsOneWidget);
|
expect(find.byKey(const Key('mine-vip-open')), findsOneWidget);
|
||||||
expect(find.byKey(const Key('mine-menu')), findsOneWidget);
|
expect(find.byKey(const Key('mine-menu')), findsOneWidget);
|
||||||
final title = tester.widget<Text>(
|
|
||||||
find.descendant(
|
|
||||||
of: find.byKey(const Key('mine-page-surface')),
|
|
||||||
matching: find.text('My'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(title.style?.fontSize, AppDimens.minePageTitle);
|
|
||||||
expect(title.style?.fontWeight, FontWeight.w700);
|
|
||||||
expect(find.byKey(const Key('mine-mini-player')), findsNothing);
|
expect(find.byKey(const Key('mine-mini-player')), findsNothing);
|
||||||
final surface = tester.widget<DecoratedBox>(
|
final surface = tester.widget<DecoratedBox>(
|
||||||
find.byKey(const Key('mine-page-surface')),
|
find.byKey(const Key('mine-page-surface')),
|
||||||
@@ -62,6 +54,25 @@ void main() {
|
|||||||
AppColors.mineGradientMiddle,
|
AppColors.mineGradientMiddle,
|
||||||
AppColors.mineGradientBottom,
|
AppColors.mineGradientBottom,
|
||||||
]);
|
]);
|
||||||
|
expect(background.stops, const [0, 0.4, 0.75]);
|
||||||
|
expect(
|
||||||
|
find.descendant(
|
||||||
|
of: find.byKey(const Key('mine-page-surface')),
|
||||||
|
matching: find.text('My'),
|
||||||
|
),
|
||||||
|
findsNothing,
|
||||||
|
);
|
||||||
|
final glow = tester.widget<DecoratedBox>(
|
||||||
|
find.byKey(const Key('mine-page-glow')),
|
||||||
|
);
|
||||||
|
final glowDecoration = glow.decoration as BoxDecoration;
|
||||||
|
final radialGlow = glowDecoration.gradient! as RadialGradient;
|
||||||
|
expect(radialGlow.center, const Alignment(0, -0.95));
|
||||||
|
expect(radialGlow.radius, 0.9);
|
||||||
|
expect(radialGlow.colors, const [
|
||||||
|
AppColors.mineGradientGlow,
|
||||||
|
AppColors.transparent,
|
||||||
|
]);
|
||||||
for (final oldShortcut in const [
|
for (final oldShortcut in const [
|
||||||
Key('mine-watchlist'),
|
Key('mine-watchlist'),
|
||||||
Key('mine-history-shortcut'),
|
Key('mine-history-shortcut'),
|
||||||
@@ -83,6 +94,49 @@ void main() {
|
|||||||
for (final entryKey in entryKeys) {
|
for (final entryKey in entryKeys) {
|
||||||
expect(find.byKey(entryKey), findsOneWidget);
|
expect(find.byKey(entryKey), findsOneWidget);
|
||||||
}
|
}
|
||||||
|
const coloredIcons = [
|
||||||
|
(
|
||||||
|
Key('mine-wallet'),
|
||||||
|
Icons.account_balance_wallet_outlined,
|
||||||
|
AppColors.mineIconWallet,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Key('mine-downloads'),
|
||||||
|
Icons.download_outlined,
|
||||||
|
AppColors.mineIconDownloads,
|
||||||
|
),
|
||||||
|
(Key('mine-history'), Icons.history_rounded, AppColors.mineIconHistory),
|
||||||
|
(
|
||||||
|
Key('mine-watch-preferences'),
|
||||||
|
Icons.tune_rounded,
|
||||||
|
AppColors.mineIconPreferences,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Key('mine-help'),
|
||||||
|
Icons.support_agent_rounded,
|
||||||
|
AppColors.mineIconSupport,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Key('mine-language'),
|
||||||
|
Icons.language_rounded,
|
||||||
|
AppColors.mineIconLanguage,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Key('mine-settings'),
|
||||||
|
Icons.settings_outlined,
|
||||||
|
AppColors.mineIconSettings,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
for (final entry in coloredIcons) {
|
||||||
|
final icon = tester.widget<Icon>(
|
||||||
|
find.descendant(
|
||||||
|
of: find.byKey(entry.$1),
|
||||||
|
matching: find.byIcon(entry.$2),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(icon.color, entry.$3);
|
||||||
|
expect(icon.size, AppDimens.mineGridIcon);
|
||||||
|
}
|
||||||
for (var index = 0; index < 8; index++) {
|
for (var index = 0; index < 8; index++) {
|
||||||
expect(find.byKey(Key('mine-menu-cell-$index')), findsOneWidget);
|
expect(find.byKey(Key('mine-menu-cell-$index')), findsOneWidget);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user