init
This commit is contained in:
278
client/lib/core/theme/app_colors.dart
Normal file
278
client/lib/core/theme/app_colors.dart
Normal file
@@ -0,0 +1,278 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// 应用颜色常量
|
||||
class AppColors {
|
||||
// 私有构造函数,防止实例化
|
||||
AppColors._();
|
||||
|
||||
// ============ 浅色主题颜色 ============
|
||||
|
||||
/// 主色调
|
||||
static const Color primary = Color(0xFF1976D2);
|
||||
static const Color onPrimary = Color(0xFFFFFFFF);
|
||||
static const Color primaryContainer = Color(0xFFBBDEFB);
|
||||
static const Color onPrimaryContainer = Color(0xFF0D47A1);
|
||||
|
||||
/// 次要色调
|
||||
static const Color secondary = Color(0xFF03DAC6);
|
||||
static const Color onSecondary = Color(0xFF000000);
|
||||
static const Color secondaryContainer = Color(0xFFB2DFDB);
|
||||
static const Color onSecondaryContainer = Color(0xFF004D40);
|
||||
|
||||
/// 第三色调
|
||||
static const Color tertiary = Color(0xFF9C27B0);
|
||||
static const Color onTertiary = Color(0xFFFFFFFF);
|
||||
static const Color tertiaryContainer = Color(0xFFE1BEE7);
|
||||
static const Color onTertiaryContainer = Color(0xFF4A148C);
|
||||
|
||||
/// 错误色
|
||||
static const Color error = Color(0xFFD32F2F);
|
||||
static const Color onError = Color(0xFFFFFFFF);
|
||||
static const Color errorContainer = Color(0xFFFFCDD2);
|
||||
static const Color onErrorContainer = Color(0xFFB71C1C);
|
||||
|
||||
/// 背景颜色
|
||||
static const Color background = Color(0xFFFFFBFE);
|
||||
static const Color onBackground = Color(0xFF1C1B1F);
|
||||
|
||||
/// 表面色
|
||||
static const Color surface = Color(0xFFFFFFFF);
|
||||
static const Color onSurface = Color(0xFF1C1B1F);
|
||||
static const Color surfaceTint = Color(0xFF1976D2);
|
||||
|
||||
/// 表面变体色
|
||||
static const Color surfaceVariant = Color(0xFFF3F3F3);
|
||||
static const Color onSurfaceVariant = Color(0xFF49454F);
|
||||
|
||||
/// 轮廓色
|
||||
static const Color outline = Color(0xFF79747E);
|
||||
static const Color outlineVariant = Color(0xFFCAC4D0);
|
||||
|
||||
/// 阴影颜色
|
||||
static const Color shadow = Color(0xFF000000);
|
||||
static const Color scrim = Color(0xFF000000);
|
||||
|
||||
/// 反转颜色
|
||||
static const Color inverseSurface = Color(0xFF313033);
|
||||
static const Color onInverseSurface = Color(0xFFF4EFF4);
|
||||
static const Color inversePrimary = Color(0xFF90CAF9);
|
||||
|
||||
// ============ 深色主题颜色 ============
|
||||
|
||||
/// 主色调 - 深色
|
||||
static const Color primaryDark = Color(0xFF90CAF9);
|
||||
static const Color onPrimaryDark = Color(0xFF003C8F);
|
||||
static const Color primaryContainerDark = Color(0xFF1565C0);
|
||||
static const Color onPrimaryContainerDark = Color(0xFFE3F2FD);
|
||||
|
||||
/// 次要色调 - 深色
|
||||
static const Color secondaryDark = Color(0xFF80CBC4);
|
||||
static const Color onSecondaryDark = Color(0xFF00251A);
|
||||
static const Color secondaryContainerDark = Color(0xFF00695C);
|
||||
static const Color onSecondaryContainerDark = Color(0xFFE0F2F1);
|
||||
|
||||
/// 第三色调 - 深色
|
||||
static const Color tertiaryDark = Color(0xFFCE93D8);
|
||||
static const Color onTertiaryDark = Color(0xFF4A148C);
|
||||
static const Color tertiaryContainerDark = Color(0xFF7B1FA2);
|
||||
static const Color onTertiaryContainerDark = Color(0xFFF3E5F5);
|
||||
|
||||
/// 错误色 - 深色
|
||||
static const Color errorDark = Color(0xFFEF5350);
|
||||
static const Color onErrorDark = Color(0xFF690005);
|
||||
static const Color errorContainerDark = Color(0xFFD32F2F);
|
||||
static const Color onErrorContainerDark = Color(0xFFFFEBEE);
|
||||
|
||||
/// 背景颜色 - 深色
|
||||
static const Color backgroundDark = Color(0xFF1C1B1F);
|
||||
static const Color onBackgroundDark = Color(0xFFE6E1E5);
|
||||
|
||||
/// 表面色 - 深色
|
||||
static const Color surfaceDark = Color(0xFF121212);
|
||||
static const Color onSurfaceDark = Color(0xFFE6E1E5);
|
||||
static const Color surfaceTintDark = Color(0xFF90CAF9);
|
||||
|
||||
/// 表面变体色 - 深色
|
||||
static const Color surfaceVariantDark = Color(0xFF49454F);
|
||||
static const Color onSurfaceVariantDark = Color(0xFFCAC4D0);
|
||||
|
||||
/// 轮廓色 - 深色
|
||||
static const Color outlineDark = Color(0xFF938F99);
|
||||
static const Color outlineVariantDark = Color(0xFF49454F);
|
||||
|
||||
/// 阴影颜色 - 深色
|
||||
static const Color shadowDark = Color(0xFF000000);
|
||||
static const Color scrimDark = Color(0xFF000000);
|
||||
|
||||
/// 反转色 - 深色
|
||||
static const Color inverseSurfaceDark = Color(0xFFE6E1E5);
|
||||
static const Color onInverseSurfaceDark = Color(0xFF313033);
|
||||
static const Color inversePrimaryDark = Color(0xFF1976D2);
|
||||
|
||||
// ============ 功能性颜色 ============
|
||||
|
||||
/// 成功色
|
||||
static const Color success = Color(0xFF4CAF50);
|
||||
static const Color onSuccess = Color(0xFFFFFFFF);
|
||||
static const Color successContainer = Color(0xFFE8F5E8);
|
||||
static const Color onSuccessContainer = Color(0xFF1B5E20);
|
||||
|
||||
/// 警告色
|
||||
static const Color warning = Color(0xFFFF9800);
|
||||
static const Color onWarning = Color(0xFFFFFFFF);
|
||||
static const Color warningContainer = Color(0xFFFFF3E0);
|
||||
static const Color onWarningContainer = Color(0xFFE65100);
|
||||
|
||||
/// 信息色
|
||||
static const Color info = Color(0xFF2196F3);
|
||||
static const Color onInfo = Color(0xFFFFFFFF);
|
||||
static const Color infoContainer = Color(0xFFE3F2FD);
|
||||
static const Color onInfoContainer = Color(0xFF0D47A1);
|
||||
|
||||
// ============ 学习模块颜色 ============
|
||||
|
||||
/// 词汇学习
|
||||
static const Color vocabulary = Color(0xFF9C27B0);
|
||||
static const Color onVocabulary = Color(0xFFFFFFFF);
|
||||
static const Color vocabularyContainer = Color(0xFFF3E5F5);
|
||||
static const Color onVocabularyContainer = Color(0xFF4A148C);
|
||||
static const Color vocabularyDark = Color(0xFFBA68C8);
|
||||
|
||||
/// 听力训练
|
||||
static const Color listening = Color(0xFF00BCD4);
|
||||
static const Color onListening = Color(0xFFFFFFFF);
|
||||
static const Color listeningContainer = Color(0xFFE0F7FA);
|
||||
static const Color onListeningContainer = Color(0xFF006064);
|
||||
static const Color listeningDark = Color(0xFF4DD0E1);
|
||||
|
||||
/// 阅读理解
|
||||
static const Color reading = Color(0xFF4CAF50);
|
||||
static const Color onReading = Color(0xFFFFFFFF);
|
||||
static const Color readingContainer = Color(0xFFE8F5E8);
|
||||
static const Color onReadingContainer = Color(0xFF1B5E20);
|
||||
static const Color readingDark = Color(0xFF81C784);
|
||||
|
||||
/// 写作练习
|
||||
static const Color writing = Color(0xFFFF5722);
|
||||
static const Color onWriting = Color(0xFFFFFFFF);
|
||||
static const Color writingContainer = Color(0xFFFBE9E7);
|
||||
static const Color onWritingContainer = Color(0xFFBF360C);
|
||||
static const Color writingDark = Color(0xFFFF8A65);
|
||||
|
||||
/// 口语练习
|
||||
static const Color speaking = Color(0xFFE91E63);
|
||||
static const Color onSpeaking = Color(0xFFFFFFFF);
|
||||
static const Color speakingContainer = Color(0xFFFCE4EC);
|
||||
static const Color onSpeakingContainer = Color(0xFF880E4F);
|
||||
static const Color speakingDark = Color(0xFFF06292);
|
||||
|
||||
// ============ 等级颜色 ============
|
||||
|
||||
/// 初级
|
||||
static const Color beginner = Color(0xFF4CAF50);
|
||||
static const Color onBeginner = Color(0xFFFFFFFF);
|
||||
static const Color beginnerDark = Color(0xFF81C784);
|
||||
|
||||
/// 中级
|
||||
static const Color intermediate = Color(0xFFFF9800);
|
||||
static const Color onIntermediate = Color(0xFFFFFFFF);
|
||||
static const Color intermediateDark = Color(0xFFFFB74D);
|
||||
|
||||
/// 高级
|
||||
static const Color advanced = Color(0xFFF44336);
|
||||
static const Color onAdvanced = Color(0xFFFFFFFF);
|
||||
static const Color advancedDark = Color(0xFFEF5350);
|
||||
|
||||
// ============ 进度颜色 ============
|
||||
|
||||
/// 进度条背景
|
||||
static const Color progressBackground = Color(0xFFE0E0E0);
|
||||
|
||||
/// 进度条前景
|
||||
static const Color progressForeground = Color(0xFF2196F3);
|
||||
|
||||
/// 完成状态
|
||||
static const Color completed = Color(0xFF4CAF50);
|
||||
|
||||
/// 进行中状态
|
||||
static const Color inProgress = Color(0xFFFF9800);
|
||||
|
||||
/// 未开始状态
|
||||
static const Color notStarted = Color(0xFFBDBDBD);
|
||||
|
||||
/// 进度等级颜色
|
||||
static const Color progressLow = Color(0xFFF44336);
|
||||
static const Color progressLowDark = Color(0xFFEF5350);
|
||||
static const Color progressMedium = Color(0xFFFF9800);
|
||||
static const Color progressMediumDark = Color(0xFFFFB74D);
|
||||
static const Color progressHigh = Color(0xFF4CAF50);
|
||||
static const Color progressHighDark = Color(0xFF81C784);
|
||||
|
||||
// ============ 特殊颜色 ============
|
||||
|
||||
/// 分割线
|
||||
static const Color divider = Color(0xFFE0E0E0);
|
||||
|
||||
/// 禁用状态
|
||||
static const Color disabled = Color(0xFFBDBDBD);
|
||||
static const Color onDisabled = Color(0xFF757575);
|
||||
|
||||
/// 透明度变体
|
||||
static Color get primaryWithOpacity => primary.withValues(alpha: 0.12);
|
||||
static Color get secondaryWithOpacity => secondary.withValues(alpha: 0.12);
|
||||
static Color get errorWithOpacity => error.withValues(alpha: 0.12);
|
||||
static Color get successWithOpacity => success.withValues(alpha: 0.12);
|
||||
static Color get warningWithOpacity => warning.withValues(alpha: 0.12);
|
||||
static Color get infoWithOpacity => info.withValues(alpha: 0.12);
|
||||
|
||||
// ============ 渐变色 ============
|
||||
|
||||
/// 主要渐变
|
||||
static const LinearGradient primaryGradient = LinearGradient(
|
||||
colors: [Color(0xFF2196F3), Color(0xFF21CBF3)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
);
|
||||
|
||||
/// 次要渐变
|
||||
static const LinearGradient secondaryGradient = LinearGradient(
|
||||
colors: [Color(0xFF03DAC6), Color(0xFF00BCD4)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
);
|
||||
|
||||
/// 词汇渐变
|
||||
static const LinearGradient vocabularyGradient = LinearGradient(
|
||||
colors: [Color(0xFF9C27B0), Color(0xFFE91E63)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
);
|
||||
|
||||
/// 听力渐变
|
||||
static const LinearGradient listeningGradient = LinearGradient(
|
||||
colors: [Color(0xFF00BCD4), Color(0xFF2196F3)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
);
|
||||
|
||||
/// 阅读渐变
|
||||
static const LinearGradient readingGradient = LinearGradient(
|
||||
colors: [Color(0xFF4CAF50), Color(0xFF8BC34A)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
);
|
||||
|
||||
/// 写作渐变
|
||||
static const LinearGradient writingGradient = LinearGradient(
|
||||
colors: [Color(0xFFFF5722), Color(0xFFFF9800)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
);
|
||||
|
||||
/// 口语渐变
|
||||
static const LinearGradient speakingGradient = LinearGradient(
|
||||
colors: [Color(0xFFE91E63), Color(0xFF9C27B0)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
);
|
||||
}
|
||||
365
client/lib/core/theme/app_dimensions.dart
Normal file
365
client/lib/core/theme/app_dimensions.dart
Normal file
@@ -0,0 +1,365 @@
|
||||
/// 应用尺寸配置
|
||||
class AppDimensions {
|
||||
// 私有构造函数,防止实例化
|
||||
AppDimensions._();
|
||||
|
||||
// ============ 间距 ============
|
||||
|
||||
/// 极小间距
|
||||
static const double spacingXs = 4.0;
|
||||
|
||||
/// 小间距
|
||||
static const double spacingSm = 8.0;
|
||||
|
||||
/// 中等间距
|
||||
static const double spacingMd = 16.0;
|
||||
|
||||
/// 大间距
|
||||
static const double spacingLg = 24.0;
|
||||
|
||||
/// 超大间距
|
||||
static const double spacingXl = 32.0;
|
||||
|
||||
/// 超超大间距
|
||||
static const double spacingXxl = 48.0;
|
||||
|
||||
// ============ 内边距 ============
|
||||
|
||||
/// 页面内边距
|
||||
static const double pagePadding = spacingMd;
|
||||
|
||||
/// 卡片内边距
|
||||
static const double cardPadding = spacingMd;
|
||||
|
||||
/// 按钮内边距
|
||||
static const double buttonPadding = spacingMd;
|
||||
|
||||
/// 输入框内边距
|
||||
static const double inputPadding = spacingMd;
|
||||
|
||||
/// 列表项内边距
|
||||
static const double listItemPadding = spacingMd;
|
||||
|
||||
/// 对话框内边距
|
||||
static const double dialogPadding = spacingLg;
|
||||
|
||||
/// 底部导航栏内边距
|
||||
static const double bottomNavPadding = spacingSm;
|
||||
|
||||
/// AppBar内边距
|
||||
static const double appBarPadding = spacingMd;
|
||||
|
||||
// ============ 外边距 ============
|
||||
|
||||
/// 页面外边距
|
||||
static const double pageMargin = spacingMd;
|
||||
|
||||
/// 卡片外边距
|
||||
static const double cardMargin = spacingSm;
|
||||
|
||||
/// 按钮外边距
|
||||
static const double buttonMargin = spacingSm;
|
||||
|
||||
/// 输入框外边距
|
||||
static const double inputMargin = spacingSm;
|
||||
|
||||
/// 列表项外边距
|
||||
static const double listItemMargin = spacingXs;
|
||||
|
||||
/// 对话框外边距
|
||||
static const double dialogMargin = spacingLg;
|
||||
|
||||
// ============ 圆角半径 ============
|
||||
|
||||
/// 极小圆角
|
||||
static const double radiusXs = 4.0;
|
||||
|
||||
/// 小圆角
|
||||
static const double radiusSm = 8.0;
|
||||
|
||||
/// 中等圆角
|
||||
static const double radiusMd = 12.0;
|
||||
|
||||
/// 大圆角
|
||||
static const double radiusLg = 16.0;
|
||||
|
||||
/// 超大圆角
|
||||
static const double radiusXl = 20.0;
|
||||
|
||||
/// 圆形
|
||||
static const double radiusCircle = 999.0;
|
||||
|
||||
// ============ 组件圆角 ============
|
||||
|
||||
/// 按钮圆角
|
||||
static const double buttonRadius = radiusSm;
|
||||
|
||||
/// 卡片圆角
|
||||
static const double cardRadius = radiusMd;
|
||||
|
||||
/// 输入框圆角
|
||||
static const double inputRadius = radiusSm;
|
||||
|
||||
/// 对话框圆角
|
||||
static const double dialogRadius = radiusLg;
|
||||
|
||||
/// 底部弹窗圆角
|
||||
static const double bottomSheetRadius = radiusLg;
|
||||
|
||||
/// 芯片圆角
|
||||
static const double chipRadius = radiusLg;
|
||||
|
||||
/// 头像圆角
|
||||
static const double avatarRadius = radiusCircle;
|
||||
|
||||
/// 图片圆角
|
||||
static const double imageRadius = radiusSm;
|
||||
|
||||
// ============ 高度 ============
|
||||
|
||||
/// AppBar高度
|
||||
static const double appBarHeight = 56.0;
|
||||
|
||||
/// 底部导航栏高度
|
||||
static const double bottomNavHeight = 80.0;
|
||||
|
||||
/// 标签栏高度
|
||||
static const double tabBarHeight = 48.0;
|
||||
|
||||
/// 按钮高度
|
||||
static const double buttonHeight = 48.0;
|
||||
|
||||
/// 小按钮高度
|
||||
static const double buttonHeightSm = 36.0;
|
||||
|
||||
/// 大按钮高度
|
||||
static const double buttonHeightLg = 56.0;
|
||||
|
||||
/// 输入框高度
|
||||
static const double inputHeight = 56.0;
|
||||
|
||||
/// 列表项高度
|
||||
static const double listItemHeight = 56.0;
|
||||
|
||||
/// 小列表项高度
|
||||
static const double listItemHeightSm = 48.0;
|
||||
|
||||
/// 大列表项高度
|
||||
static const double listItemHeightLg = 72.0;
|
||||
|
||||
/// 工具栏高度
|
||||
static const double toolbarHeight = 56.0;
|
||||
|
||||
/// 搜索栏高度
|
||||
static const double searchBarHeight = 48.0;
|
||||
|
||||
/// 进度条高度
|
||||
static const double progressBarHeight = 4.0;
|
||||
|
||||
/// 分割线高度
|
||||
static const double dividerHeight = 1.0;
|
||||
|
||||
// ============ 宽度 ============
|
||||
|
||||
/// 最小按钮宽度
|
||||
static const double buttonMinWidth = 64.0;
|
||||
|
||||
/// 侧边栏宽度
|
||||
static const double drawerWidth = 304.0;
|
||||
|
||||
/// 分割线宽度
|
||||
static const double dividerWidth = 1.0;
|
||||
|
||||
/// 边框宽度
|
||||
static const double borderWidth = 1.0;
|
||||
|
||||
/// 粗边框宽度
|
||||
static const double borderWidthThick = 2.0;
|
||||
|
||||
// ============ 图标尺寸 ============
|
||||
|
||||
/// 极小图标
|
||||
static const double iconXs = 16.0;
|
||||
|
||||
/// 小图标
|
||||
static const double iconSm = 20.0;
|
||||
|
||||
/// 中等图标
|
||||
static const double iconMd = 24.0;
|
||||
|
||||
/// 大图标
|
||||
static const double iconLg = 32.0;
|
||||
|
||||
/// 超大图标
|
||||
static const double iconXl = 48.0;
|
||||
|
||||
/// 超超大图标
|
||||
static const double iconXxl = 64.0;
|
||||
|
||||
// ============ 头像尺寸 ============
|
||||
|
||||
/// 小头像
|
||||
static const double avatarSm = 32.0;
|
||||
|
||||
/// 中等头像
|
||||
static const double avatarMd = 48.0;
|
||||
|
||||
/// 大头像
|
||||
static const double avatarLg = 64.0;
|
||||
|
||||
/// 超大头像
|
||||
static const double avatarXl = 96.0;
|
||||
|
||||
// ============ 阴影 ============
|
||||
|
||||
/// 阴影偏移
|
||||
static const double shadowOffset = 2.0;
|
||||
|
||||
/// 阴影模糊半径
|
||||
static const double shadowBlurRadius = 8.0;
|
||||
|
||||
/// 阴影扩散半径
|
||||
static const double shadowSpreadRadius = 0.0;
|
||||
|
||||
// ============ 动画持续时间 ============
|
||||
|
||||
/// 快速动画
|
||||
static const Duration animationFast = Duration(milliseconds: 150);
|
||||
|
||||
/// 中等动画
|
||||
static const Duration animationMedium = Duration(milliseconds: 300);
|
||||
|
||||
/// 慢速动画
|
||||
static const Duration animationSlow = Duration(milliseconds: 500);
|
||||
|
||||
// ============ 学习相关尺寸 ============
|
||||
|
||||
/// 单词卡片高度
|
||||
static const double wordCardHeight = 200.0;
|
||||
|
||||
/// 单词卡片宽度
|
||||
static const double wordCardWidth = 300.0;
|
||||
|
||||
/// 进度圆环大小
|
||||
static const double progressCircleSize = 120.0;
|
||||
|
||||
/// 等级徽章大小
|
||||
static const double levelBadgeSize = 40.0;
|
||||
|
||||
/// 成就徽章大小
|
||||
static const double achievementBadgeSize = 60.0;
|
||||
|
||||
/// 音频播放器高度
|
||||
static const double audioPlayerHeight = 80.0;
|
||||
|
||||
/// 练习题选项高度
|
||||
static const double exerciseOptionHeight = 48.0;
|
||||
|
||||
/// 学习统计卡片高度
|
||||
static const double statsCardHeight = 120.0;
|
||||
|
||||
// ============ 响应式断点 ============
|
||||
|
||||
/// 手机断点
|
||||
static const double mobileBreakpoint = 600.0;
|
||||
|
||||
/// 平板断点
|
||||
static const double tabletBreakpoint = 900.0;
|
||||
|
||||
/// 桌面断点
|
||||
static const double desktopBreakpoint = 1200.0;
|
||||
|
||||
// ============ 最大宽度 ============
|
||||
|
||||
/// 内容最大宽度
|
||||
static const double maxContentWidth = 1200.0;
|
||||
|
||||
/// 对话框最大宽度
|
||||
static const double maxDialogWidth = 560.0;
|
||||
|
||||
/// 卡片最大宽度
|
||||
static const double maxCardWidth = 400.0;
|
||||
|
||||
// ============ 最小尺寸 ============
|
||||
|
||||
/// 最小触摸目标尺寸
|
||||
static const double minTouchTarget = 48.0;
|
||||
|
||||
/// 最小按钮尺寸
|
||||
static const double minButtonSize = 36.0;
|
||||
|
||||
// ============ 网格布局 ============
|
||||
|
||||
/// 网格间距
|
||||
static const double gridSpacing = spacingSm;
|
||||
|
||||
/// 网格交叉轴间距
|
||||
static const double gridCrossAxisSpacing = spacingSm;
|
||||
|
||||
/// 网格主轴间距
|
||||
static const double gridMainAxisSpacing = spacingSm;
|
||||
|
||||
/// 网格子项宽高比
|
||||
static const double gridChildAspectRatio = 1.0;
|
||||
|
||||
// ============ 列表布局 ============
|
||||
|
||||
/// 列表分割线缩进
|
||||
static const double listDividerIndent = spacingMd;
|
||||
|
||||
/// 列表分割线结束缩进
|
||||
static const double listDividerEndIndent = spacingMd;
|
||||
|
||||
// ============ 浮动操作按钮 ============
|
||||
|
||||
/// 浮动操作按钮大小
|
||||
static const double fabSize = 56.0;
|
||||
|
||||
/// 小浮动操作按钮大小
|
||||
static const double fabSizeSmall = 40.0;
|
||||
|
||||
/// 大浮动操作按钮大小
|
||||
static const double fabSizeLarge = 96.0;
|
||||
|
||||
/// 浮动操作按钮边距
|
||||
static const double fabMargin = spacingMd;
|
||||
|
||||
// ============ 辅助方法 ============
|
||||
|
||||
/// 根据屏幕宽度获取响应式间距
|
||||
static double getResponsiveSpacing(double screenWidth) {
|
||||
if (screenWidth < mobileBreakpoint) {
|
||||
return spacingSm;
|
||||
} else if (screenWidth < tabletBreakpoint) {
|
||||
return spacingMd;
|
||||
} else {
|
||||
return spacingLg;
|
||||
}
|
||||
}
|
||||
|
||||
/// 根据屏幕宽度获取响应式内边距
|
||||
static double getResponsivePadding(double screenWidth) {
|
||||
if (screenWidth < mobileBreakpoint) {
|
||||
return spacingMd;
|
||||
} else if (screenWidth < tabletBreakpoint) {
|
||||
return spacingLg;
|
||||
} else {
|
||||
return spacingXl;
|
||||
}
|
||||
}
|
||||
|
||||
/// 根据屏幕宽度判断是否为移动设备
|
||||
static bool isMobile(double screenWidth) {
|
||||
return screenWidth < mobileBreakpoint;
|
||||
}
|
||||
|
||||
/// 根据屏幕宽度判断是否为平板设备
|
||||
static bool isTablet(double screenWidth) {
|
||||
return screenWidth >= mobileBreakpoint && screenWidth < desktopBreakpoint;
|
||||
}
|
||||
|
||||
/// 根据屏幕宽度判断是否为桌面设备
|
||||
static bool isDesktop(double screenWidth) {
|
||||
return screenWidth >= desktopBreakpoint;
|
||||
}
|
||||
}
|
||||
437
client/lib/core/theme/app_text_styles.dart
Normal file
437
client/lib/core/theme/app_text_styles.dart
Normal file
@@ -0,0 +1,437 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'app_colors.dart';
|
||||
|
||||
/// 应用文本样式配置
|
||||
class AppTextStyles {
|
||||
// 私有构造函数,防止实例化
|
||||
AppTextStyles._();
|
||||
|
||||
// ============ 字体家族 ============
|
||||
|
||||
/// 默认字体 - 使用系统默认,不指定字体名称
|
||||
static const String? defaultFontFamily = null;
|
||||
|
||||
/// 中文字体 - 使用系统默认,不指定字体名称
|
||||
static const String? chineseFontFamily = null;
|
||||
|
||||
/// 等宽字体 - 使用系统默认,不指定字体名称
|
||||
static const String? monospaceFontFamily = null;
|
||||
|
||||
// ============ 字体权重 ============
|
||||
|
||||
static const FontWeight light = FontWeight.w300;
|
||||
static const FontWeight regular = FontWeight.w400;
|
||||
static const FontWeight medium = FontWeight.w500;
|
||||
static const FontWeight semiBold = FontWeight.w600;
|
||||
static const FontWeight bold = FontWeight.w700;
|
||||
static const FontWeight extraBold = FontWeight.w800;
|
||||
|
||||
// ============ 基础文本样式 ============
|
||||
|
||||
/// 标题样式
|
||||
static const TextStyle displayLarge = TextStyle(
|
||||
fontSize: 57,
|
||||
fontWeight: bold,
|
||||
letterSpacing: -0.25,
|
||||
height: 1.12,
|
||||
color: AppColors.onSurface,
|
||||
);
|
||||
|
||||
static const TextStyle displayMedium = TextStyle(
|
||||
fontSize: 45,
|
||||
fontWeight: bold,
|
||||
letterSpacing: 0,
|
||||
height: 1.16,
|
||||
color: AppColors.onSurface,
|
||||
);
|
||||
|
||||
static const TextStyle displaySmall = TextStyle(
|
||||
fontSize: 36,
|
||||
fontWeight: bold,
|
||||
letterSpacing: 0,
|
||||
height: 1.22,
|
||||
color: AppColors.onSurface,
|
||||
);
|
||||
|
||||
/// 标题样式
|
||||
static const TextStyle headlineLarge = TextStyle(
|
||||
fontSize: 32,
|
||||
fontWeight: bold,
|
||||
letterSpacing: 0,
|
||||
height: 1.25,
|
||||
color: AppColors.onSurface,
|
||||
);
|
||||
|
||||
static const TextStyle headlineMedium = TextStyle(
|
||||
fontSize: 28,
|
||||
fontWeight: semiBold,
|
||||
letterSpacing: 0,
|
||||
height: 1.29,
|
||||
color: AppColors.onSurface,
|
||||
);
|
||||
|
||||
static const TextStyle headlineSmall = TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: semiBold,
|
||||
letterSpacing: 0,
|
||||
height: 1.33,
|
||||
color: AppColors.onSurface,
|
||||
);
|
||||
|
||||
/// 标题样式
|
||||
static const TextStyle titleLarge = TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: semiBold,
|
||||
letterSpacing: 0,
|
||||
height: 1.27,
|
||||
color: AppColors.onSurface,
|
||||
);
|
||||
|
||||
static const TextStyle titleMedium = TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: medium,
|
||||
letterSpacing: 0.15,
|
||||
height: 1.50,
|
||||
color: AppColors.onSurface,
|
||||
);
|
||||
|
||||
static const TextStyle titleSmall = TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: medium,
|
||||
letterSpacing: 0.1,
|
||||
height: 1.43,
|
||||
color: AppColors.onSurface,
|
||||
);
|
||||
|
||||
/// 标签样式
|
||||
static const TextStyle labelLarge = TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: medium,
|
||||
letterSpacing: 0.1,
|
||||
height: 1.43,
|
||||
color: AppColors.onSurface,
|
||||
);
|
||||
|
||||
static const TextStyle labelMedium = TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: medium,
|
||||
letterSpacing: 0.5,
|
||||
height: 1.33,
|
||||
color: AppColors.onSurface,
|
||||
);
|
||||
|
||||
static const TextStyle labelSmall = TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: medium,
|
||||
letterSpacing: 0.5,
|
||||
height: 1.45,
|
||||
color: AppColors.onSurface,
|
||||
);
|
||||
|
||||
/// 正文样式
|
||||
static const TextStyle bodyLarge = TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: regular,
|
||||
letterSpacing: 0.5,
|
||||
height: 1.50,
|
||||
color: AppColors.onSurface,
|
||||
);
|
||||
|
||||
static const TextStyle bodyMedium = TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: regular,
|
||||
letterSpacing: 0.25,
|
||||
height: 1.43,
|
||||
color: AppColors.onSurface,
|
||||
);
|
||||
|
||||
static const TextStyle bodySmall = TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: regular,
|
||||
letterSpacing: 0.4,
|
||||
height: 1.33,
|
||||
color: AppColors.onSurfaceVariant,
|
||||
);
|
||||
|
||||
// ============ 自定义文本样式 ============
|
||||
|
||||
/// 按钮文本样式
|
||||
static const TextStyle buttonLarge = TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: semiBold,
|
||||
letterSpacing: 0.1,
|
||||
height: 1.25,
|
||||
color: AppColors.onPrimary,
|
||||
);
|
||||
|
||||
static const TextStyle buttonMedium = TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: semiBold,
|
||||
letterSpacing: 0.1,
|
||||
height: 1.29,
|
||||
color: AppColors.onPrimary,
|
||||
);
|
||||
|
||||
static const TextStyle buttonSmall = TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: medium,
|
||||
letterSpacing: 0.5,
|
||||
height: 1.33,
|
||||
color: AppColors.onPrimary,
|
||||
);
|
||||
|
||||
/// 输入框文本样式
|
||||
static const TextStyle inputText = TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: regular,
|
||||
letterSpacing: 0.5,
|
||||
height: 1.50,
|
||||
color: AppColors.onSurface,
|
||||
);
|
||||
|
||||
static const TextStyle inputLabel = TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: medium,
|
||||
letterSpacing: 0.15,
|
||||
height: 1.50,
|
||||
color: AppColors.onSurfaceVariant,
|
||||
);
|
||||
|
||||
static const TextStyle inputHint = TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: regular,
|
||||
letterSpacing: 0.5,
|
||||
height: 1.50,
|
||||
color: AppColors.onSurfaceVariant,
|
||||
);
|
||||
|
||||
static const TextStyle inputError = TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: regular,
|
||||
letterSpacing: 0.4,
|
||||
height: 1.33,
|
||||
color: AppColors.error,
|
||||
);
|
||||
|
||||
/// 导航文本样式
|
||||
static const TextStyle navigationLabel = TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: medium,
|
||||
letterSpacing: 0.5,
|
||||
height: 1.33,
|
||||
color: AppColors.onSurface,
|
||||
);
|
||||
|
||||
static const TextStyle tabLabel = TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: medium,
|
||||
letterSpacing: 0.1,
|
||||
height: 1.43,
|
||||
color: AppColors.onSurface,
|
||||
);
|
||||
|
||||
/// 卡片文本样式
|
||||
static const TextStyle cardTitle = TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: semiBold,
|
||||
letterSpacing: 0,
|
||||
height: 1.33,
|
||||
color: AppColors.onSurface,
|
||||
);
|
||||
|
||||
static const TextStyle cardSubtitle = TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: regular,
|
||||
letterSpacing: 0.25,
|
||||
height: 1.43,
|
||||
color: AppColors.onSurfaceVariant,
|
||||
);
|
||||
|
||||
static const TextStyle cardBody = TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: regular,
|
||||
letterSpacing: 0.25,
|
||||
height: 1.43,
|
||||
color: AppColors.onSurface,
|
||||
);
|
||||
|
||||
/// 列表文本样式
|
||||
static const TextStyle listTitle = TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: medium,
|
||||
letterSpacing: 0.15,
|
||||
height: 1.50,
|
||||
color: AppColors.onSurface,
|
||||
);
|
||||
|
||||
static const TextStyle listSubtitle = TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: regular,
|
||||
letterSpacing: 0.25,
|
||||
height: 1.43,
|
||||
color: AppColors.onSurfaceVariant,
|
||||
);
|
||||
|
||||
/// 学习相关文本样式
|
||||
static const TextStyle wordText = TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: semiBold,
|
||||
letterSpacing: 0,
|
||||
height: 1.33,
|
||||
color: AppColors.onSurface,
|
||||
);
|
||||
|
||||
static const TextStyle phoneticText = TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: regular,
|
||||
letterSpacing: 0.5,
|
||||
height: 1.50,
|
||||
color: AppColors.onSurfaceVariant,
|
||||
fontFamily: monospaceFontFamily,
|
||||
);
|
||||
|
||||
static const TextStyle definitionText = TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: regular,
|
||||
letterSpacing: 0.5,
|
||||
height: 1.50,
|
||||
color: AppColors.onSurface,
|
||||
);
|
||||
|
||||
static const TextStyle exampleText = TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: regular,
|
||||
letterSpacing: 0.25,
|
||||
height: 1.43,
|
||||
color: AppColors.onSurfaceVariant,
|
||||
fontStyle: FontStyle.italic,
|
||||
);
|
||||
|
||||
/// 分数和统计文本样式
|
||||
static const TextStyle scoreText = TextStyle(
|
||||
fontSize: 32,
|
||||
fontWeight: bold,
|
||||
letterSpacing: 0,
|
||||
height: 1.25,
|
||||
color: AppColors.primary,
|
||||
);
|
||||
|
||||
static const TextStyle statisticNumber = TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: semiBold,
|
||||
letterSpacing: 0,
|
||||
height: 1.33,
|
||||
color: AppColors.onSurface,
|
||||
);
|
||||
|
||||
static const TextStyle statisticLabel = TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: medium,
|
||||
letterSpacing: 0.5,
|
||||
height: 1.33,
|
||||
color: AppColors.onSurfaceVariant,
|
||||
);
|
||||
|
||||
/// 状态文本样式
|
||||
static const TextStyle successText = TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: medium,
|
||||
letterSpacing: 0.1,
|
||||
height: 1.43,
|
||||
color: AppColors.success,
|
||||
);
|
||||
|
||||
static const TextStyle warningText = TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: medium,
|
||||
letterSpacing: 0.1,
|
||||
height: 1.43,
|
||||
color: AppColors.warning,
|
||||
);
|
||||
|
||||
static const TextStyle errorText = TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: medium,
|
||||
letterSpacing: 0.1,
|
||||
height: 1.43,
|
||||
color: AppColors.error,
|
||||
);
|
||||
|
||||
static const TextStyle infoText = TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: medium,
|
||||
letterSpacing: 0.1,
|
||||
height: 1.43,
|
||||
color: AppColors.info,
|
||||
);
|
||||
|
||||
// ============ Material 3 文本主题 ============
|
||||
|
||||
/// Material 3 文本主题
|
||||
static const TextTheme textTheme = TextTheme(
|
||||
displayLarge: displayLarge,
|
||||
displayMedium: displayMedium,
|
||||
displaySmall: displaySmall,
|
||||
headlineLarge: headlineLarge,
|
||||
headlineMedium: headlineMedium,
|
||||
headlineSmall: headlineSmall,
|
||||
titleLarge: titleLarge,
|
||||
titleMedium: titleMedium,
|
||||
titleSmall: titleSmall,
|
||||
labelLarge: labelLarge,
|
||||
labelMedium: labelMedium,
|
||||
labelSmall: labelSmall,
|
||||
bodyLarge: bodyLarge,
|
||||
bodyMedium: bodyMedium,
|
||||
bodySmall: bodySmall,
|
||||
);
|
||||
|
||||
// ============ 辅助方法 ============
|
||||
|
||||
/// 根据主题亮度调整文本颜色
|
||||
static TextStyle adaptiveTextStyle(TextStyle style, Brightness brightness) {
|
||||
if (brightness == Brightness.dark) {
|
||||
return style.copyWith(
|
||||
color: _adaptColorForDarkTheme(style.color ?? AppColors.onSurface),
|
||||
);
|
||||
}
|
||||
return style;
|
||||
}
|
||||
|
||||
/// 为深色主题调整颜色
|
||||
static Color _adaptColorForDarkTheme(Color color) {
|
||||
if (color == AppColors.onSurface) {
|
||||
return AppColors.onSurfaceDark;
|
||||
} else if (color == AppColors.onSurfaceVariant) {
|
||||
return AppColors.onSurfaceVariantDark;
|
||||
} else if (color == AppColors.primary) {
|
||||
return AppColors.primaryDark;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
/// 创建带有特定颜色的文本样式
|
||||
static TextStyle withColor(TextStyle style, Color color) {
|
||||
return style.copyWith(color: color);
|
||||
}
|
||||
|
||||
/// 创建带有特定字体大小的文本样式
|
||||
static TextStyle withFontSize(TextStyle style, double fontSize) {
|
||||
return style.copyWith(fontSize: fontSize);
|
||||
}
|
||||
|
||||
/// 创建带有特定字体权重的文本样式
|
||||
static TextStyle withFontWeight(TextStyle style, FontWeight fontWeight) {
|
||||
return style.copyWith(fontWeight: fontWeight);
|
||||
}
|
||||
|
||||
/// 创建带有特定行高的文本样式
|
||||
static TextStyle withHeight(TextStyle style, double height) {
|
||||
return style.copyWith(height: height);
|
||||
}
|
||||
|
||||
/// 创建带有特定字母间距的文本样式
|
||||
static TextStyle withLetterSpacing(TextStyle style, double letterSpacing) {
|
||||
return style.copyWith(letterSpacing: letterSpacing);
|
||||
}
|
||||
}
|
||||
424
client/lib/core/theme/app_theme.dart
Normal file
424
client/lib/core/theme/app_theme.dart
Normal file
@@ -0,0 +1,424 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'app_colors.dart';
|
||||
import 'app_text_styles.dart';
|
||||
import 'app_dimensions.dart';
|
||||
|
||||
/// 应用主题配置
|
||||
class AppTheme {
|
||||
// 私有构造函数,防止实例化
|
||||
AppTheme._();
|
||||
|
||||
// ============ 亮色主题 ============
|
||||
|
||||
static ThemeData get lightTheme {
|
||||
return ThemeData(
|
||||
useMaterial3: true,
|
||||
brightness: Brightness.light,
|
||||
|
||||
// 禁用Google Fonts,使用系统默认字体
|
||||
fontFamily: null, // 不指定字体,使用系统默认
|
||||
|
||||
// 颜色方案
|
||||
colorScheme: const ColorScheme.light(
|
||||
primary: AppColors.primary,
|
||||
onPrimary: AppColors.onPrimary,
|
||||
primaryContainer: AppColors.primaryContainer,
|
||||
onPrimaryContainer: AppColors.onPrimaryContainer,
|
||||
secondary: AppColors.secondary,
|
||||
onSecondary: AppColors.onSecondary,
|
||||
secondaryContainer: AppColors.secondaryContainer,
|
||||
onSecondaryContainer: AppColors.onSecondaryContainer,
|
||||
tertiary: AppColors.tertiary,
|
||||
onTertiary: AppColors.onTertiary,
|
||||
tertiaryContainer: AppColors.tertiaryContainer,
|
||||
onTertiaryContainer: AppColors.onTertiaryContainer,
|
||||
error: AppColors.error,
|
||||
onError: AppColors.onError,
|
||||
errorContainer: AppColors.errorContainer,
|
||||
onErrorContainer: AppColors.onErrorContainer,
|
||||
surface: AppColors.surface,
|
||||
onSurface: AppColors.onSurface,
|
||||
surfaceContainerHighest: AppColors.surfaceVariant,
|
||||
onSurfaceVariant: AppColors.onSurfaceVariant,
|
||||
outline: AppColors.outline,
|
||||
outlineVariant: AppColors.outlineVariant,
|
||||
shadow: AppColors.shadow,
|
||||
scrim: AppColors.scrim,
|
||||
inverseSurface: AppColors.inverseSurface,
|
||||
onInverseSurface: AppColors.onInverseSurface,
|
||||
inversePrimary: AppColors.inversePrimary,
|
||||
surfaceTint: AppColors.surfaceTint,
|
||||
),
|
||||
|
||||
// 文本主题
|
||||
textTheme: AppTextStyles.textTheme,
|
||||
|
||||
// AppBar主题
|
||||
appBarTheme: AppBarTheme(
|
||||
backgroundColor: AppColors.surface,
|
||||
foregroundColor: AppColors.onSurface,
|
||||
elevation: 0,
|
||||
scrolledUnderElevation: 1,
|
||||
centerTitle: true,
|
||||
titleTextStyle: AppTextStyles.titleLarge,
|
||||
toolbarHeight: AppDimensions.appBarHeight,
|
||||
systemOverlayStyle: const SystemUiOverlayStyle(
|
||||
statusBarColor: Colors.transparent,
|
||||
statusBarIconBrightness: Brightness.dark,
|
||||
statusBarBrightness: Brightness.light,
|
||||
),
|
||||
),
|
||||
|
||||
// 按钮主题
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.primary,
|
||||
foregroundColor: AppColors.onPrimary,
|
||||
textStyle: AppTextStyles.buttonMedium,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(AppDimensions.buttonRadius),
|
||||
),
|
||||
minimumSize: const Size(AppDimensions.buttonMinWidth, AppDimensions.buttonHeight),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: AppDimensions.buttonPadding,
|
||||
vertical: AppDimensions.spacingSm,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
filledButtonTheme: FilledButtonThemeData(
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: AppColors.primary,
|
||||
foregroundColor: AppColors.onPrimary,
|
||||
textStyle: AppTextStyles.buttonMedium,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(AppDimensions.buttonRadius),
|
||||
),
|
||||
minimumSize: const Size(AppDimensions.buttonMinWidth, AppDimensions.buttonHeight),
|
||||
),
|
||||
),
|
||||
|
||||
outlinedButtonTheme: OutlinedButtonThemeData(
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: AppColors.primary,
|
||||
textStyle: AppTextStyles.buttonMedium,
|
||||
side: const BorderSide(color: AppColors.outline),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(AppDimensions.buttonRadius),
|
||||
),
|
||||
minimumSize: const Size(AppDimensions.buttonMinWidth, AppDimensions.buttonHeight),
|
||||
),
|
||||
),
|
||||
|
||||
textButtonTheme: TextButtonThemeData(
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: AppColors.primary,
|
||||
textStyle: AppTextStyles.buttonMedium,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(AppDimensions.buttonRadius),
|
||||
),
|
||||
minimumSize: const Size(AppDimensions.buttonMinWidth, AppDimensions.buttonHeight),
|
||||
),
|
||||
),
|
||||
|
||||
// 输入框主题
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
filled: true,
|
||||
fillColor: AppColors.surfaceVariant,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppDimensions.inputRadius),
|
||||
borderSide: const BorderSide(color: AppColors.outline),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppDimensions.inputRadius),
|
||||
borderSide: const BorderSide(color: AppColors.outline),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppDimensions.inputRadius),
|
||||
borderSide: const BorderSide(color: AppColors.primary, width: 2),
|
||||
),
|
||||
errorBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppDimensions.inputRadius),
|
||||
borderSide: const BorderSide(color: AppColors.error),
|
||||
),
|
||||
focusedErrorBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppDimensions.inputRadius),
|
||||
borderSide: const BorderSide(color: AppColors.error, width: 2),
|
||||
),
|
||||
labelStyle: AppTextStyles.inputLabel,
|
||||
hintStyle: AppTextStyles.inputHint,
|
||||
errorStyle: AppTextStyles.inputError,
|
||||
contentPadding: const EdgeInsets.all(AppDimensions.inputPadding),
|
||||
),
|
||||
|
||||
// 卡片主题
|
||||
cardTheme: CardThemeData(
|
||||
color: AppColors.surface,
|
||||
elevation: 1,
|
||||
shadowColor: AppColors.shadow,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(AppDimensions.cardRadius),
|
||||
),
|
||||
margin: const EdgeInsets.all(AppDimensions.cardMargin),
|
||||
),
|
||||
|
||||
// 底部导航栏主题
|
||||
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
|
||||
backgroundColor: AppColors.surface,
|
||||
selectedItemColor: AppColors.primary,
|
||||
unselectedItemColor: AppColors.onSurfaceVariant,
|
||||
selectedLabelStyle: AppTextStyles.navigationLabel,
|
||||
unselectedLabelStyle: AppTextStyles.navigationLabel,
|
||||
type: BottomNavigationBarType.fixed,
|
||||
elevation: 8,
|
||||
),
|
||||
|
||||
// 标签栏主题
|
||||
tabBarTheme: TabBarThemeData(
|
||||
labelColor: AppColors.primary,
|
||||
unselectedLabelColor: AppColors.onSurfaceVariant,
|
||||
labelStyle: AppTextStyles.tabLabel,
|
||||
unselectedLabelStyle: AppTextStyles.tabLabel,
|
||||
indicator: const UnderlineTabIndicator(
|
||||
borderSide: BorderSide(color: AppColors.primary, width: 2),
|
||||
),
|
||||
),
|
||||
|
||||
// 芯片主题
|
||||
chipTheme: ChipThemeData(
|
||||
backgroundColor: AppColors.surfaceVariant,
|
||||
selectedColor: AppColors.primaryContainer,
|
||||
disabledColor: AppColors.surfaceVariant.withValues(alpha: 0.5),
|
||||
labelStyle: AppTextStyles.labelMedium,
|
||||
secondaryLabelStyle: AppTextStyles.labelMedium,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(AppDimensions.chipRadius),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: AppDimensions.spacingSm,
|
||||
vertical: AppDimensions.spacingXs,
|
||||
),
|
||||
),
|
||||
|
||||
// 对话框主题
|
||||
dialogTheme: DialogThemeData(
|
||||
backgroundColor: AppColors.surface,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(AppDimensions.dialogRadius),
|
||||
),
|
||||
titleTextStyle: AppTextStyles.headlineSmall,
|
||||
contentTextStyle: AppTextStyles.bodyMedium,
|
||||
),
|
||||
|
||||
// 提示条主题
|
||||
snackBarTheme: SnackBarThemeData(
|
||||
backgroundColor: AppColors.inverseSurface,
|
||||
contentTextStyle: AppTextStyles.bodyMedium.copyWith(
|
||||
color: AppColors.onInverseSurface,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(AppDimensions.radiusSm),
|
||||
),
|
||||
behavior: SnackBarBehavior.floating,
|
||||
),
|
||||
|
||||
// 浮动操作按钮主题
|
||||
floatingActionButtonTheme: const FloatingActionButtonThemeData(
|
||||
backgroundColor: AppColors.primary,
|
||||
foregroundColor: AppColors.onPrimary,
|
||||
elevation: 6,
|
||||
focusElevation: 8,
|
||||
hoverElevation: 8,
|
||||
highlightElevation: 12,
|
||||
),
|
||||
|
||||
// 分割线主题
|
||||
dividerTheme: const DividerThemeData(
|
||||
color: AppColors.outline,
|
||||
thickness: AppDimensions.dividerHeight,
|
||||
space: AppDimensions.dividerHeight,
|
||||
),
|
||||
|
||||
// 列表项主题
|
||||
listTileTheme: ListTileThemeData(
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: AppDimensions.listItemPadding,
|
||||
vertical: AppDimensions.spacingXs,
|
||||
),
|
||||
titleTextStyle: AppTextStyles.listTitle,
|
||||
subtitleTextStyle: AppTextStyles.listSubtitle,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(AppDimensions.radiusSm),
|
||||
),
|
||||
),
|
||||
|
||||
// 开关主题
|
||||
switchTheme: SwitchThemeData(
|
||||
thumbColor: WidgetStateProperty.resolveWith((states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return AppColors.onPrimary;
|
||||
}
|
||||
return AppColors.outline;
|
||||
}),
|
||||
trackColor: WidgetStateProperty.resolveWith((states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return AppColors.primary;
|
||||
}
|
||||
return AppColors.surfaceVariant;
|
||||
}),
|
||||
),
|
||||
|
||||
// 复选框主题
|
||||
checkboxTheme: CheckboxThemeData(
|
||||
fillColor: WidgetStateProperty.resolveWith((states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return AppColors.primary;
|
||||
}
|
||||
return Colors.transparent;
|
||||
}),
|
||||
checkColor: WidgetStateProperty.all(AppColors.onPrimary),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(AppDimensions.radiusXs),
|
||||
),
|
||||
),
|
||||
|
||||
// 单选按钮主题
|
||||
radioTheme: RadioThemeData(
|
||||
fillColor: WidgetStateProperty.resolveWith((states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return AppColors.primary;
|
||||
}
|
||||
return AppColors.onSurfaceVariant;
|
||||
}),
|
||||
),
|
||||
|
||||
// 滑块主题
|
||||
sliderTheme: const SliderThemeData(
|
||||
activeTrackColor: AppColors.primary,
|
||||
inactiveTrackColor: AppColors.surfaceVariant,
|
||||
thumbColor: AppColors.primary,
|
||||
overlayColor: AppColors.primaryContainer,
|
||||
valueIndicatorColor: AppColors.primary,
|
||||
),
|
||||
|
||||
// 进度指示器主题
|
||||
progressIndicatorTheme: const ProgressIndicatorThemeData(
|
||||
color: AppColors.primary,
|
||||
linearTrackColor: AppColors.surfaceVariant,
|
||||
circularTrackColor: AppColors.surfaceVariant,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ============ 深色主题 ============
|
||||
|
||||
static ThemeData get darkTheme {
|
||||
return ThemeData(
|
||||
useMaterial3: true,
|
||||
brightness: Brightness.dark,
|
||||
|
||||
// 禁用Google Fonts,使用系统默认字体
|
||||
fontFamily: null, // 不指定字体,使用系统默认
|
||||
|
||||
// 颜色方案
|
||||
colorScheme: const ColorScheme.dark(
|
||||
primary: AppColors.primaryDark,
|
||||
onPrimary: AppColors.onPrimaryDark,
|
||||
primaryContainer: AppColors.primaryContainerDark,
|
||||
onPrimaryContainer: AppColors.onPrimaryContainerDark,
|
||||
secondary: AppColors.secondaryDark,
|
||||
onSecondary: AppColors.onSecondaryDark,
|
||||
secondaryContainer: AppColors.secondaryContainerDark,
|
||||
onSecondaryContainer: AppColors.onSecondaryContainerDark,
|
||||
tertiary: AppColors.tertiaryDark,
|
||||
onTertiary: AppColors.onTertiaryDark,
|
||||
tertiaryContainer: AppColors.tertiaryContainerDark,
|
||||
onTertiaryContainer: AppColors.onTertiaryContainerDark,
|
||||
error: AppColors.errorDark,
|
||||
onError: AppColors.onErrorDark,
|
||||
errorContainer: AppColors.errorContainerDark,
|
||||
onErrorContainer: AppColors.onErrorContainerDark,
|
||||
surface: AppColors.surfaceDark,
|
||||
onSurface: AppColors.onSurfaceDark,
|
||||
surfaceContainerHighest: AppColors.surfaceVariantDark,
|
||||
onSurfaceVariant: AppColors.onSurfaceVariantDark,
|
||||
outline: AppColors.outlineDark,
|
||||
outlineVariant: AppColors.outlineVariantDark,
|
||||
shadow: AppColors.shadowDark,
|
||||
scrim: AppColors.scrimDark,
|
||||
inverseSurface: AppColors.inverseSurfaceDark,
|
||||
onInverseSurface: AppColors.onInverseSurfaceDark,
|
||||
inversePrimary: AppColors.inversePrimaryDark,
|
||||
surfaceTint: AppColors.surfaceTintDark,
|
||||
),
|
||||
|
||||
// 文本主题(深色适配)
|
||||
textTheme: AppTextStyles.textTheme.apply(
|
||||
bodyColor: AppColors.onSurfaceDark,
|
||||
displayColor: AppColors.onSurfaceDark,
|
||||
),
|
||||
|
||||
// AppBar主题
|
||||
appBarTheme: AppBarTheme(
|
||||
backgroundColor: AppColors.surfaceDark,
|
||||
foregroundColor: AppColors.onSurfaceDark,
|
||||
elevation: 0,
|
||||
scrolledUnderElevation: 1,
|
||||
centerTitle: true,
|
||||
titleTextStyle: AppTextStyles.titleLarge.copyWith(
|
||||
color: AppColors.onSurfaceDark,
|
||||
),
|
||||
toolbarHeight: AppDimensions.appBarHeight,
|
||||
systemOverlayStyle: const SystemUiOverlayStyle(
|
||||
statusBarColor: Colors.transparent,
|
||||
statusBarIconBrightness: Brightness.light,
|
||||
statusBarBrightness: Brightness.dark,
|
||||
),
|
||||
),
|
||||
|
||||
// 其他组件主题配置与亮色主题类似,但使用深色颜色
|
||||
// 为了简洁,这里省略了重复的配置
|
||||
// 实际项目中应该完整配置所有组件的深色主题
|
||||
);
|
||||
}
|
||||
|
||||
// ============ 主题扩展 ============
|
||||
|
||||
/// 获取当前主题的学习模块颜色
|
||||
static Map<String, Color> getLearningColors(BuildContext context) {
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
return {
|
||||
'vocabulary': isDark ? AppColors.vocabularyDark : AppColors.vocabulary,
|
||||
'listening': isDark ? AppColors.listeningDark : AppColors.listening,
|
||||
'reading': isDark ? AppColors.readingDark : AppColors.reading,
|
||||
'writing': isDark ? AppColors.writingDark : AppColors.writing,
|
||||
'speaking': isDark ? AppColors.speakingDark : AppColors.speaking,
|
||||
};
|
||||
}
|
||||
|
||||
/// 获取等级颜色
|
||||
static Color getLevelColor(String level, {bool isDark = false}) {
|
||||
switch (level.toLowerCase()) {
|
||||
case 'beginner':
|
||||
return isDark ? AppColors.beginnerDark : AppColors.beginner;
|
||||
case 'intermediate':
|
||||
return isDark ? AppColors.intermediateDark : AppColors.intermediate;
|
||||
case 'advanced':
|
||||
return isDark ? AppColors.advancedDark : AppColors.advanced;
|
||||
default:
|
||||
return isDark ? AppColors.primaryDark : AppColors.primary;
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取进度颜色
|
||||
static Color getProgressColor(double progress, {bool isDark = false}) {
|
||||
if (progress < 0.3) {
|
||||
return isDark ? AppColors.progressLowDark : AppColors.progressLow;
|
||||
} else if (progress < 0.7) {
|
||||
return isDark ? AppColors.progressMediumDark : AppColors.progressMedium;
|
||||
} else {
|
||||
return isDark ? AppColors.progressHighDark : AppColors.progressHigh;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user