refactor(theme): 迁移主题感知颜色至 ThemeExtension

- 创建 AppThemeColors ThemeExtension 类,统一管理主题感知颜色(涨跌色、卡片背景、渐变等)
- 从 AppColorScheme 移除主题感知辅助函数,仅保留静态颜色常量
- 在 AppTheme 中注册 ThemeExtension,支持深色/浅色主题工厂
- 重构所有 UI 组件使用 context.appColors 访问主题颜色,替代硬编码的 AppColorScheme 方法调用
- 移除组件中重复的 isDark 判断逻辑,简化颜色获取方式
- 保持向后兼容性,所有现有功能不变
This commit is contained in:
2026-04-06 01:58:08 +08:00
parent 396668aa43
commit 7ed2435a4c
36 changed files with 658 additions and 810 deletions

View File

@@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import '../../../../core/theme/app_color_scheme.dart';
import '../../../../core/theme/app_spacing.dart';
import '../../../../core/theme/app_theme.dart';
import '../../../../core/theme/app_theme_extension.dart';
import '../../../../data/models/coin.dart';
import 'coin_avatar.dart';
@@ -25,21 +25,16 @@ class CoinSelector extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final isDark = Theme.of(context).brightness == Brightness.dark;
return GestureDetector(
onTap: () => _showCoinPicker(context),
child: Container(
width: double.infinity,
padding: const EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: isDark
? colorScheme.surfaceContainer
: colorScheme.surfaceContainerLowest,
color: context.appColors.surfaceCard,
borderRadius: BorderRadius.circular(AppRadius.lg),
border: Border.all(
color: colorScheme.outlineVariant.withOpacity(0.15),
color: context.appColors.ghostBorder,
),
),
child: Row(
@@ -60,14 +55,14 @@ class CoinSelector extends StatelessWidget {
Text(
selectedCoin?.name ?? '点击选择交易对',
style: AppTextStyles.bodyMedium(context).copyWith(
color: colorScheme.onSurfaceVariant,
color: context.colors.onSurfaceVariant,
),
),
],
),
// 下拉箭头
Icon(LucideIcons.chevronDown,
size: 16, color: colorScheme.onSurfaceVariant),
size: 16, color: context.colors.onSurfaceVariant),
],
),
),
@@ -75,9 +70,6 @@ class CoinSelector extends StatelessWidget {
}
void _showCoinPicker(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final isDark = Theme.of(context).brightness == Brightness.dark;
showModalBottomSheet(
context: context,
backgroundColor: const Color(0x00000000),
@@ -85,9 +77,9 @@ class CoinSelector extends StatelessWidget {
builder: (ctx) => Container(
height: MediaQuery.of(ctx).size.height * 0.65,
decoration: BoxDecoration(
color: isDark
? colorScheme.surface
: colorScheme.surfaceContainerLowest,
color: ctx.isDark
? ctx.colors.surface
: ctx.colors.surfaceContainerLowest,
borderRadius:
BorderRadius.vertical(top: Radius.circular(AppRadius.xxl)),
),
@@ -99,7 +91,7 @@ class CoinSelector extends StatelessWidget {
width: 40,
height: 4,
decoration: BoxDecoration(
color: colorScheme.onSurfaceVariant.withOpacity(0.3),
color: ctx.colors.onSurfaceVariant.withValues(alpha: 0.3),
borderRadius: BorderRadius.circular(AppRadius.sm),
),
),
@@ -114,12 +106,12 @@ class CoinSelector extends StatelessWidget {
GestureDetector(
onTap: () => Navigator.of(ctx).pop(),
child: Icon(LucideIcons.x,
color: colorScheme.onSurfaceVariant),
color: ctx.colors.onSurfaceVariant),
),
],
),
),
Divider(height: 1, color: colorScheme.outlineVariant.withOpacity(0.2)),
Divider(height: 1, color: ctx.colors.outlineVariant.withValues(alpha: 0.2)),
// 币种列表
Expanded(
child: ListView.builder(
@@ -137,12 +129,10 @@ class CoinSelector extends StatelessWidget {
Widget _buildCoinItem(
Coin coin, BuildContext context, BuildContext sheetContext) {
final colorScheme = Theme.of(context).colorScheme;
final isDark = Theme.of(context).brightness == Brightness.dark;
final isSelected = selectedCoin?.code == coin.code;
final changeColor = coin.isUp
? AppColorScheme.getUpColor(isDark)
: AppColorScheme.getDownColor(isDark);
? context.appColors.up
: context.appColors.down;
return GestureDetector(
onTap: () {
@@ -153,7 +143,7 @@ class CoinSelector extends StatelessWidget {
padding: EdgeInsets.symmetric(
horizontal: AppSpacing.lg, vertical: AppSpacing.md),
color:
isSelected ? colorScheme.primary.withOpacity(0.1) : const Color(0x00000000),
isSelected ? context.colors.primary.withValues(alpha: 0.1) : const Color(0x00000000),
child: Row(
children: [
CoinAvatar(icon: coin.displayIcon),
@@ -170,7 +160,7 @@ class CoinSelector extends StatelessWidget {
SizedBox(width: AppSpacing.xs),
Text('/USDT',
style: AppTextStyles.bodySmall(context).copyWith(
color: colorScheme.onSurfaceVariant,
color: context.colors.onSurfaceVariant,
)),
const Spacer(),
Text('\$${coin.formattedPrice}',
@@ -180,7 +170,7 @@ class CoinSelector extends StatelessWidget {
Container(
padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: changeColor.withOpacity(0.1),
color: changeColor.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(AppRadius.sm),
),
child: Text(coin.formattedChange,
@@ -192,7 +182,7 @@ class CoinSelector extends StatelessWidget {
if (isSelected) ...[
SizedBox(width: AppSpacing.sm),
Icon(LucideIcons.check,
size: 16, color: colorScheme.primary),
size: 16, color: context.colors.primary),
],
],
),
@@ -200,7 +190,7 @@ class CoinSelector extends StatelessWidget {
// 第二行:币种名称
Text(coin.name,
style: AppTextStyles.bodyMedium(context).copyWith(
color: colorScheme.onSurfaceVariant,
color: context.colors.onSurfaceVariant,
)),
],
),

View File

@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import '../../../../core/theme/app_color_scheme.dart';
import '../../../../core/theme/app_spacing.dart';
import '../../../../core/theme/app_theme.dart';
import '../../../../core/theme/app_theme_extension.dart';
import '../../../components/glass_panel.dart';
import '../../../components/neon_glow.dart';
@@ -27,11 +27,9 @@ class ConfirmDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final isDark = Theme.of(context).brightness == Brightness.dark;
final actionColor = isBuy
? AppColorScheme.getUpColor(isDark)
: AppColorScheme.getDownColor(isDark);
? context.appColors.up
: context.appColors.down;
return Dialog(
backgroundColor: const Color(0x00000000),
@@ -49,14 +47,14 @@ class ConfirmDialog extends StatelessWidget {
),
),
SizedBox(height: AppSpacing.lg),
_dialogRow(context, '交易对', '$coinCode/USDT', colorScheme),
_dialogRow(context, '交易对', '$coinCode/USDT'),
SizedBox(height: AppSpacing.sm),
_dialogRow(context, '委托价格', '$price USDT', colorScheme),
_dialogRow(context, '委托价格', '$price USDT'),
SizedBox(height: AppSpacing.sm),
_dialogRow(context, '交易金额', '$amount USDT', colorScheme,
_dialogRow(context, '交易金额', '$amount USDT',
valueColor: actionColor),
SizedBox(height: AppSpacing.sm),
_dialogRow(context, '交易数量', '$quantity $coinCode', colorScheme),
_dialogRow(context, '交易数量', '$quantity $coinCode'),
SizedBox(height: AppSpacing.lg),
Row(
children: [
@@ -87,7 +85,7 @@ class ConfirmDialog extends StatelessWidget {
);
}
Widget _dialogRow(BuildContext context, String label, String value, ColorScheme colorScheme,
Widget _dialogRow(BuildContext context, String label, String value,
{Color? valueColor}) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -95,11 +93,11 @@ class ConfirmDialog extends StatelessWidget {
Text(label,
style: AppTextStyles.headlineMedium(context).copyWith(
fontWeight: FontWeight.w400,
color: colorScheme.onSurfaceVariant,
color: context.colors.onSurfaceVariant,
)),
Text(value,
style: AppTextStyles.numberMedium(context).copyWith(
color: valueColor ?? colorScheme.onSurface,
color: valueColor ?? context.colors.onSurface,
)),
],
);

View File

@@ -1,38 +1,34 @@
import 'package:flutter/material.dart';
import '../../../../core/theme/app_spacing.dart';
import '../../../../core/theme/app_theme.dart';
import '../../../../core/theme/app_theme_extension.dart';
/// 占位卡片组件
///
/// 当未选择币种时显示的占位提示卡片。
class PlaceholderCard extends StatelessWidget {
final String message;
final ColorScheme colorScheme;
const PlaceholderCard({
super.key,
required this.message,
required this.colorScheme,
});
@override
Widget build(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
return Container(
width: double.infinity,
padding: const EdgeInsets.all(AppSpacing.xl),
decoration: BoxDecoration(
color: isDark
? colorScheme.surfaceContainer
: colorScheme.surfaceContainerLowest,
color: context.appColors.surfaceCard,
borderRadius: BorderRadius.circular(AppRadius.lg),
border: Border.all(
color: colorScheme.outlineVariant.withOpacity(0.15),
color: context.appColors.ghostBorder,
),
),
child: Center(
child: Text(message,
style: AppTextStyles.headlineMedium(context).copyWith(
color: colorScheme.onSurfaceVariant,
color: context.colors.onSurfaceVariant,
)),
),
);

View File

@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import '../../../../core/theme/app_color_scheme.dart';
import '../../../../core/theme/app_spacing.dart';
import '../../../../core/theme/app_theme.dart';
import '../../../../core/theme/app_theme_extension.dart';
import '../../../../data/models/coin.dart';
/// 价格卡片组件
@@ -14,25 +14,21 @@ class PriceCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final isDark = Theme.of(context).brightness == Brightness.dark;
final isUp = coin.isUp;
final changeColor =
isUp ? AppColorScheme.getUpColor(isDark) : AppColorScheme.getDownColor(isDark);
isUp ? context.appColors.up : context.appColors.down;
final changeBgColor = isUp
? AppColorScheme.getUpBackgroundColor(isDark)
: AppColorScheme.getDownBackgroundColor(isDark);
? context.appColors.upBackground
: context.appColors.downBackground;
return Container(
width: double.infinity,
padding: const EdgeInsets.all(AppSpacing.md + AppSpacing.sm),
decoration: BoxDecoration(
color: isDark
? colorScheme.surfaceContainer
: colorScheme.surfaceContainerLowest,
color: context.appColors.surfaceCard,
borderRadius: BorderRadius.circular(AppRadius.lg),
border: Border.all(
color: colorScheme.outlineVariant.withOpacity(0.15),
color: context.appColors.ghostBorder,
),
),
child: Column(

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import '../../../../core/theme/app_color_scheme.dart';
import '../../../../core/theme/app_spacing.dart';
import '../../../../core/theme/app_theme.dart';
import '../../../../core/theme/app_theme_extension.dart';
import '../../../../data/models/coin.dart';
import 'amount_input.dart';
@@ -37,17 +38,13 @@ class TradeFormCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final isDark = Theme.of(context).brightness == Brightness.dark;
final isBuy = tradeType == 0;
final actionColor = isBuy
? AppColorScheme.getUpColor(isDark)
: AppColorScheme.getDownColor(isDark);
? context.appColors.up
: context.appColors.down;
// 设计稿中 card 背景色
final cardBgColor = isDark
? colorScheme.surfaceContainer
: colorScheme.surfaceContainerLowest;
final cardBgColor = context.appColors.surfaceCard;
return Container(
width: double.infinity,
@@ -56,7 +53,7 @@ class TradeFormCard extends StatelessWidget {
color: cardBgColor,
borderRadius: BorderRadius.circular(AppRadius.lg),
border: Border.all(
color: colorScheme.outlineVariant.withOpacity(0.15),
color: context.appColors.ghostBorder,
),
),
child: Column(
@@ -83,7 +80,7 @@ class TradeFormCard extends StatelessWidget {
border: isBuy
? null
: Border.all(
color: colorScheme.outlineVariant.withOpacity(0.15)),
color: context.appColors.ghostBorder),
),
child: Center(
child: Text(
@@ -91,7 +88,7 @@ class TradeFormCard extends StatelessWidget {
style: AppTextStyles.headlineMedium(context).copyWith(
color: isBuy
? AppColorScheme.darkOnPrimary
: colorScheme.onSurfaceVariant,
: context.colors.onSurfaceVariant,
),
),
),
@@ -113,7 +110,7 @@ class TradeFormCard extends StatelessWidget {
border: !isBuy
? null
: Border.all(
color: colorScheme.outlineVariant.withOpacity(0.15)),
color: context.appColors.ghostBorder),
),
child: Center(
child: Text(
@@ -121,7 +118,7 @@ class TradeFormCard extends StatelessWidget {
style: AppTextStyles.headlineMedium(context).copyWith(
color: !isBuy
? AppColorScheme.darkOnPrimary
: colorScheme.onSurfaceVariant,
: context.colors.onSurfaceVariant,
),
),
),
@@ -139,7 +136,7 @@ class TradeFormCard extends StatelessWidget {
children: [
Text('交易金额',
style: AppTextStyles.bodyMedium(context).copyWith(
color: colorScheme.onSurfaceVariant,
color: context.colors.onSurfaceVariant,
)),
Text('USDT',
style: AppTextStyles.labelLarge(context)),
@@ -163,7 +160,7 @@ class TradeFormCard extends StatelessWidget {
? '可用: $availableUsdt USDT'
: '可用: $availableCoinQty ${selectedCoin?.code ?? ""}',
style: AppTextStyles.bodySmall(context).copyWith(
color: colorScheme.onSurfaceVariant,
color: context.colors.onSurfaceVariant,
),
),
const SizedBox(height: AppSpacing.md),
@@ -172,13 +169,13 @@ class TradeFormCard extends StatelessWidget {
// 设计稿gap:8圆角smbg-tertiary高32
Row(
children: [
_buildPctButton(context, '25%', 0.25, colorScheme),
_buildPctButton(context, '25%', 0.25),
const SizedBox(width: AppSpacing.sm),
_buildPctButton(context, '50%', 0.5, colorScheme),
_buildPctButton(context, '50%', 0.5),
const SizedBox(width: AppSpacing.sm),
_buildPctButton(context, '75%', 0.75, colorScheme),
_buildPctButton(context, '75%', 0.75),
const SizedBox(width: AppSpacing.sm),
_buildPctButton(context, '100%', 1.0, colorScheme),
_buildPctButton(context, '100%', 1.0),
],
),
const SizedBox(height: AppSpacing.md + AppSpacing.sm),
@@ -189,7 +186,7 @@ class TradeFormCard extends StatelessWidget {
children: [
Text('交易数量',
style: AppTextStyles.bodyMedium(context).copyWith(
color: colorScheme.onSurfaceVariant,
color: context.colors.onSurfaceVariant,
)),
Text(
'$calculatedQuantity ${selectedCoin?.code ?? ''}',
@@ -203,20 +200,20 @@ class TradeFormCard extends StatelessWidget {
}
/// 百分比按钮 - 设计稿圆角smbg-tertiary高32
Widget _buildPctButton(BuildContext context, String label, double pct, ColorScheme colorScheme) {
Widget _buildPctButton(BuildContext context, String label, double pct) {
return Expanded(
child: GestureDetector(
onTap: () => onFillPercent(pct),
child: Container(
height: 32,
decoration: BoxDecoration(
color: colorScheme.surfaceContainerHighest.withOpacity(0.5),
color: context.colors.surfaceContainerHighest.withValues(alpha: 0.5),
borderRadius: BorderRadius.circular(AppRadius.sm),
),
child: Center(
child: Text(label,
style: AppTextStyles.labelLarge(context).copyWith(
color: colorScheme.onSurfaceVariant,
color: context.colors.onSurfaceVariant,
)),
),
),