flutter `ColorScheme` 中**成对互补**的颜色
·
我来帮你梳理 ColorScheme 中成对互补的颜色关系:
🎯 核心互补对(On 系列)
| 背景色 | 前景色(互补) | 关系说明 |
|---|---|---|
primary | onPrimary | 主色背景 ↔ 主色上的文字/图标 |
primaryContainer | onPrimaryContainer | 主色容器 ↔ 容器上的内容 |
secondary | onSecondary | 辅助色背景 ↔ 辅助色上的内容 |
secondaryContainer | onSecondaryContainer | 辅助容器 ↔ 容器内容 |
tertiary | onTertiary | 点缀色背景 ↔ 点缀色上的内容 |
tertiaryContainer | onTertiaryContainer | 点缀容器 ↔ 容器内容 |
error | onError | 错误背景 ↔ 错误上的内容 |
errorContainer | onErrorContainer | 错误容器 ↔ 容器内容 |
surface | onSurface | 表面背景 ↔ 表面主要内容 |
surfaceVariant | onSurfaceVariant | 表面变体 ↔ 次要内容 |
background | onBackground | 页面背景 ↔ 页面内容 |
inverseSurface | onInverseSurface | 反色表面 ↔ 反色内容 |
🎯 层级互补对(Surface 系列)
| 层级 | 用途 | 互补关系 |
|---|---|---|
surfaceDim | 最暗表面 | ←→ surfaceBright 最亮表面 |
surfaceContainerLowest | 最低层容器 | ←→ surfaceContainerHighest 最高层 |
surfaceContainerLow | 低层容器 | ←→ surfaceContainerHigh 高层 |
surfaceContainer | 标准容器 | 中间层级 |
层级逻辑:Lowest → Low → Container → High → Highest → Bright
🎯 固定色互补对(Fixed 系列)
| 固定背景 | 固定前景 | 说明 |
|---|---|---|
primaryFixed | onPrimaryFixed | 不随主题变化的主色组合 |
primaryFixedDim | onPrimaryFixedVariant | 深固定主色 ↔ 变体文字 |
secondaryFixed | onSecondaryFixed | 固定辅助色组合 |
secondaryFixedDim | onSecondaryFixedVariant | 深固定辅助色 ↔ 变体文字 |
tertiaryFixed | onTertiaryFixed | 固定点缀色组合 |
tertiaryFixedDim | onTertiaryFixedVariant | 深固定点缀色 ↔ 变体文字 |
🎯 效果互补对
| 效果 | 用途 | 互补关系 |
|---|---|---|
shadow | 阴影颜色 | ←→ surface 被投影的表面 |
scrim | 遮罩层 | ←→ surface 被遮罩的内容 |
outline | 主要边框 | ←→ outlineVariant 次要边框 |
surfaceTint | 表面着色(叠加) | ←→ surface 被着色的表面 |
🎯 反色互补对(Inverse 系列)
| 正常模式 | 反色模式 | 关系 |
|---|---|---|
surface + onSurface | inverseSurface + onInverseSurface | 正常表面 ↔ 反色表面 |
primary | inversePrimary | 正常主色 ↔ 反色主色 |
使用场景:
- 正常:亮色背景 + 深色文字
- 反色:深色背景 + 亮色文字(如 SnackBar)
📋 完整互补关系图
ColorScheme 互补结构
├── 主色组
│ ├── primary ↔ onPrimary
│ ├── primaryContainer ↔ onPrimaryContainer
│ └── primaryFixed ↔ onPrimaryFixed
│ └── primaryFixedDim ↔ onPrimaryFixedVariant
│
├── 辅助色组
│ ├── secondary ↔ onSecondary
│ ├── secondaryContainer ↔ onSecondaryContainer
│ └── secondaryFixed ↔ onSecondaryFixed
│ └── secondaryFixedDim ↔ onSecondaryFixedVariant
│
├── 点缀色组
│ ├── tertiary ↔ onTertiary
│ ├── tertiaryContainer ↔ onTertiaryContainer
│ └── tertiaryFixed ↔ onTertiaryFixed
│ └── tertiaryFixedDim ↔ onTertiaryFixedVariant
│
├── 错误色组
│ ├── error ↔ onError
│ └── errorContainer ↔ onErrorContainer
│
├── 表面层级组(递进关系)
│ ├── surfaceDim(最暗)→ ... → surfaceBright(最亮)
│ ├── surfaceContainerLowest → ... → surfaceContainerHighest
│ └── surface + onSurface(主要)
│ └── surfaceVariant + onSurfaceVariant(次要)
│
├── 背景组
│ └── background ↔ onBackground
│
├── 反色组(与正常相反)
│ ├── inverseSurface ↔ onInverseSurface
│ └── inversePrimary(对应 primary)
│
└── 效果组
├── shadow(投影)
├── scrim(遮罩)
├── outline ↔ outlineVariant(主次边框)
└── surfaceTint(着色叠加到 surface)
💡 使用原则
| 原则 | 说明 |
|---|---|
背景用 xxx,文字用 onXxx | 保证对比度 |
容器用 xxxContainer,内容用 onXxxContainer | 柔和背景配清晰文字 |
层级从 Lowest 到 Highest | 创建视觉深度 |
| 固定色不随主题变 | 品牌标识保持一致 |
| 反色用于临时组件 | SnackBar、Toast 等 |
需要我把这些互补关系做成一个速查表代码吗?
参考代码 ,配色不好(待修改)
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../util/sp_util.dart';
///主题模式管理器 Create by lzx on 2020/3/11.
extension ThemeModeExtension on ThemeMode {
String get value => ['System', 'Light', 'Dark'][index];
}
class ThemeProvider extends ChangeNotifier {
/// 当前主题颜色
// MaterialColor primarySwatch = Colors.orange;
// MaterialColor primarySwatch = Colors.green;
MaterialColor primarySwatch = Colors.blue;
ThemeProvider() {
loadFontSize();
}
double _fontSize = 1.0; // 默认字体大
double get fontSize => _fontSize;
double effectiveTextScaleFactor(BuildContext context) {
// 获取系统的textScaleFactor,并结合用户设置的缩放比例
return MediaQuery.of(context).textScaler.scale(_fontSize).clamp(0.6, 1.4);
}
Future<void> loadFontSize() async {
final prefs = await SharedPreferences.getInstance();
_fontSize = prefs.getDouble('fontSize') ?? 1.0;
notifyListeners();
}
Future<void> saveFontSize(double size) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setDouble('fontSize', size);
_fontSize = size;
notifyListeners();
}
void syncTheme() async {
final String theme = SPUtil.getTheme();
if (theme.isNotEmpty && theme != ThemeMode.system.value) {
notifyListeners();
}
}
void setTheme(ThemeMode themeMode) async {
SPUtil.putTheme(themeMode.value);
notifyListeners();
}
ThemeMode getThemeMode() {
final String theme = SPUtil.getTheme();
//print("当前$theme");
switch (theme) {
case 'Dark':
return ThemeMode.dark;
case 'Light':
return ThemeMode.light;
default:
return ThemeMode.system;
}
}
ThemeData getTheme({bool isDarkMode = false}) {
final cs = _generateColorScheme(isDarkMode);
return ThemeData(
useMaterial3: true,
brightness: isDarkMode ? Brightness.dark : Brightness.light,
// ========== Material 3 核心配色 ==========
colorScheme: cs,
// ========== 兼容旧组件的基础颜色(全部与 ColorScheme 保持一致)==========
primaryColor: cs.primary,
primaryColorLight: primarySwatch.shade100,
primaryColorDark: primarySwatch.shade700,
// 背景系统
scaffoldBackgroundColor: cs.background,
// 页面主背景
canvasColor: cs.surface,
// 抽屉、底部弹窗背景
cardColor: cs.surface,
// 卡片背景
dialogBackgroundColor: cs.surface,
// 对话框背景
dividerColor: cs.outlineVariant,
// 分割线颜色
// 状态颜色
disabledColor: cs.onSurface.withOpacity(0.38),
// 禁用态
hintColor: cs.onSurfaceVariant,
// 提示文字
indicatorColor: cs.primary,
// 指示器(Tab等)
// 高亮/点击/悬浮/聚焦
highlightColor: cs.primary.withOpacity(0.1),
splashColor: cs.primary.withOpacity(0.1),
hoverColor: cs.primary.withOpacity(0.05),
focusColor: cs.primary.withOpacity(0.1),
// 阴影
shadowColor: cs.shadow,
// 错误色(兼容旧组件)
//errorColor: cs.error,
// ==================== AppBar 主题 ====================
appBarTheme: AppBarTheme(
elevation: 0,
scrolledUnderElevation: 3,
backgroundColor: cs.surface,
foregroundColor: cs.onSurface,
surfaceTintColor: cs.surfaceTint,
// systemOverlayStyle: isDarkMode
// ? SystemUiOverlayStyle.light
// : SystemUiOverlayStyle.dark,
iconTheme: IconThemeData(color: cs.onSurface),
actionsIconTheme: IconThemeData(color: cs.onSurface),
titleTextStyle: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
color: cs.onSurface,
),
),
// ==================== Card 卡片主题 ====================
cardTheme: CardTheme(
elevation: 0,
color: cs.surface,
surfaceTintColor: cs.surfaceTint,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
// ==================== 按钮系列(全部 M3 规范)====================
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
foregroundColor: cs.onPrimary,
backgroundColor: cs.primary,
disabledForegroundColor: cs.onSurface.withOpacity(0.38),
disabledBackgroundColor: cs.onSurface.withOpacity(0.12),
elevation: 0,
shadowColor: Colors.transparent,
surfaceTintColor: Colors.transparent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
),
),
filledButtonTheme: FilledButtonThemeData(
style: FilledButton.styleFrom(
foregroundColor: cs.onPrimary,
backgroundColor: cs.primary,
disabledForegroundColor: cs.onSurface.withOpacity(0.38),
disabledBackgroundColor: cs.onSurface.withOpacity(0.12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
),
),
outlinedButtonTheme: OutlinedButtonThemeData(
style: OutlinedButton.styleFrom(
foregroundColor: cs.primary,
disabledForegroundColor: cs.onSurface.withOpacity(0.38),
side: BorderSide(color: cs.outline),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
),
),
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
foregroundColor: cs.primary,
disabledForegroundColor: cs.onSurface.withOpacity(0.38),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
),
),
// ==================== 输入框样式 ====================
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: cs.surfaceContainerHighest,
contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: cs.primary, width: 2),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: cs.error, width: 1),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: cs.error, width: 2),
),
disabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
labelStyle: TextStyle(color: cs.onSurfaceVariant),
hintStyle: TextStyle(color: cs.onSurfaceVariant),
prefixIconColor: cs.onSurfaceVariant,
suffixIconColor: cs.onSurfaceVariant,
),
// ==================== Chip 标签 ====================
chipTheme: ChipThemeData(
backgroundColor: cs.surfaceContainer,
disabledColor: cs.onSurface.withOpacity(0.12),
selectedColor: cs.secondaryContainer,
secondarySelectedColor: cs.secondaryContainer,
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4),
shape: StadiumBorder(),
labelStyle: TextStyle(color: cs.onSurface),
secondaryLabelStyle: TextStyle(color: cs.onSecondaryContainer),
brightness: cs.brightness,
surfaceTintColor: cs.surfaceTint,
deleteIconColor: cs.onSurfaceVariant,
),
// ==================== 底部导航 ====================
navigationBarTheme: NavigationBarThemeData(
backgroundColor: cs.surfaceContainer,
elevation: 0,
height: 80,
indicatorColor: cs.secondaryContainer,
indicatorShape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
labelTextStyle: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.selected)) {
return TextStyle(
color: cs.onSurface,
fontSize: 12,
fontWeight: FontWeight.w500,
);
}
return TextStyle(
color: cs.onSurfaceVariant,
fontSize: 12,
);
}),
iconTheme: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.selected)) {
return IconThemeData(color: cs.onSecondaryContainer);
}
return IconThemeData(color: cs.onSurfaceVariant);
}),
),
// ==================== 侧边导航 ====================
navigationRailTheme: NavigationRailThemeData(
backgroundColor: cs.surface,
elevation: 0,
selectedIconTheme: IconThemeData(color: cs.onSecondaryContainer),
unselectedIconTheme: IconThemeData(color: cs.onSurfaceVariant),
selectedLabelTextStyle: TextStyle(
color: cs.onSurface,
fontWeight: FontWeight.w500,
),
unselectedLabelTextStyle: TextStyle(
color: cs.onSurfaceVariant,
),
indicatorColor: cs.secondaryContainer,
),
// ==================== Tab 栏 ====================
tabBarTheme: TabBarTheme(
indicatorColor: cs.primary,
indicatorSize: TabBarIndicatorSize.label,
labelColor: cs.primary,
unselectedLabelColor: cs.onSurfaceVariant,
dividerColor: cs.outlineVariant,
),
// ==================== 对话框 ====================
dialogTheme: DialogTheme(
backgroundColor: cs.surfaceContainerHigh,
surfaceTintColor: cs.surfaceTint,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(28),
),
),
// ==================== 底部弹窗 ====================
bottomSheetTheme: BottomSheetThemeData(
backgroundColor: cs.surfaceContainerLow,
surfaceTintColor: cs.surfaceTint,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(28)),
),
),
// ==================== 提示条 SnackBar ====================
snackBarTheme: SnackBarThemeData(
backgroundColor: cs.inverseSurface,
contentTextStyle: TextStyle(color: cs.onInverseSurface),
actionTextColor: cs.inversePrimary,
closeIconColor: cs.onInverseSurface,
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
// ==================== 悬浮按钮 ====================
floatingActionButtonTheme: FloatingActionButtonThemeData(
foregroundColor: cs.onPrimaryContainer,
backgroundColor: cs.primaryContainer,
elevation: 0,
focusElevation: 0,
hoverElevation: 0,
highlightElevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
),
// ==================== 开关 / 复选 / 单选 ====================
switchTheme: SwitchThemeData(
thumbColor: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.selected)) return cs.primary;
if (states.contains(MaterialState.disabled))
return cs.onSurface.withOpacity(0.38);
return cs.outline;
}),
trackColor: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.selected))
return cs.primary.withOpacity(0.5);
return cs.surfaceVariant;
}),
trackOutlineColor: MaterialStateProperty.all(Colors.transparent),
),
checkboxTheme: CheckboxThemeData(
fillColor: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.selected)) return cs.primary;
return Colors.transparent;
}),
checkColor: MaterialStateProperty.all(cs.onPrimary),
side: BorderSide(color: cs.outline),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4),
),
),
radioTheme: RadioThemeData(
fillColor: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.selected)) return cs.primary;
return cs.onSurfaceVariant;
}),
),
// ==================== 滑动条 ====================
sliderTheme: SliderThemeData(
activeTrackColor: cs.primary,
inactiveTrackColor: cs.surfaceContainerHighest,
thumbColor: cs.primary,
overlayColor: cs.primary.withOpacity(0.1),
valueIndicatorColor: cs.inverseSurface,
valueIndicatorTextStyle: TextStyle(color: cs.onInverseSurface),
),
// ==================== 进度条 ====================
progressIndicatorTheme: ProgressIndicatorThemeData(
color: cs.primary,
linearTrackColor: cs.surfaceContainerHighest,
circularTrackColor: cs.surfaceContainerHighest,
),
// ==================== 分割线 ====================
dividerTheme: DividerThemeData(
color: cs.outlineVariant,
thickness: 1,
space: 1,
),
// ==================== 列表项 ====================
listTileTheme: ListTileThemeData(
iconColor: cs.onSurfaceVariant,
textColor: cs.onSurface,
selectedColor: cs.primary,
selectedTileColor: cs.primary.withOpacity(0.1),
contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
// ==================== 提示框 ====================
tooltipTheme: TooltipThemeData(
decoration: BoxDecoration(
color: cs.inverseSurface,
borderRadius: BorderRadius.circular(8),
),
textStyle: TextStyle(color: cs.onInverseSurface),
),
// ==================== 文字主题 ====================
textTheme: _buildTextTheme(isDarkMode, cs),
// ==================== 图标主题 ====================
iconTheme: IconThemeData(
color: cs.onSurfaceVariant,
size: 24,
),
primaryIconTheme: IconThemeData(
color: cs.onPrimary,
size: 24,
),
);
}
// ========== 生成完整的 ColorScheme ==========
/// 根据 明暗模式 生成标准 Material3 ColorScheme
ColorScheme _generateColorScheme(bool isDarkMode) {
// ====================== 核心主色(基于你的 primarySwatch)======================
final Color primary =
isDarkMode ? primarySwatch.shade800 : primarySwatch.shade500;
final Color onPrimary = isDarkMode
? Colors.white.withValues(alpha: 0.9)
: Colors.white; // 主色上的文字 → 永远白色(最清晰)
final Color primaryContainer = isDarkMode
? primarySwatch.shade800 // 暗黑:深容器
: primarySwatch.shade100; // 亮色:浅容器
final Color onPrimaryContainer = isDarkMode
? primarySwatch.shade200 // 暗黑容器上的文字 → 亮色
: primarySwatch.shade700; // 亮色容器上的文字 → 暗色
// ====================== 辅助色 Secondary(柔和、不抢戏)======================
final Color secondary =
isDarkMode ? Colors.blueGrey.shade300 : Colors.blueGrey.shade500;
final Color onSecondary = Colors.white;
final Color secondaryContainer =
isDarkMode ? Colors.blueGrey.shade700 : Colors.blueGrey.shade100;
final Color onSecondaryContainer =
isDarkMode ? Colors.blueGrey.shade200 : Colors.blueGrey.shade700;
// ====================== 第三色 Tertiary(点缀色)======================
final Color tertiary =
isDarkMode ? primarySwatch.shade200 : primarySwatch.shade400;
final Color onTertiary = Colors.white;
final Color tertiaryContainer =
isDarkMode ? primarySwatch.shade700 : primarySwatch.shade50;
final Color onTertiaryContainer =
isDarkMode ? primarySwatch.shade200 : primarySwatch.shade600;
// ====================== 错误色(系统标准,不自己乱配)======================
final Color error = isDarkMode ? Colors.red.shade400 : Colors.red.shade600;
final Color onError = Colors.white;
final Color errorContainer =
isDarkMode ? Colors.red.shade800 : Colors.red.shade100;
final Color onErrorContainer =
isDarkMode ? Colors.red.shade200 : Colors.red.shade800;
// ====================== 背景 / 表面色(M3 标准灰度,干净舒适)======================
final Color background =
isDarkMode ? const Color(0xFF141218) : const Color(0xFFF8F9FA);
final Color onBackground =
isDarkMode ? const Color(0xFFE1E1E1) : const Color(0xFF1A1A1A);
final Color surface =
isDarkMode ? const Color(0xFF1B1A1F) : const Color(0xFFFFFFFF);
final Color onSurface =
isDarkMode ? const Color(0xFFE1E1E1) : const Color(0xFF1A1A1A);
final Color surfaceVariant =
isDarkMode ? const Color(0xFF45464F) : const Color(0xFFE0E2E8);
final Color onSurfaceVariant =
isDarkMode ? const Color(0xFFC4C6CF) : const Color(0xFF45464F);
// Surface 层级(卡片、弹窗、对话框 自动分层)
final Color surfaceDim =
isDarkMode ? const Color(0xFF141218) : const Color(0xFFE0E2E8);
final Color surfaceBright =
isDarkMode ? const Color(0xFF35363F) : const Color(0xFFFFFFFF);
final Color surfaceContainerLowest =
isDarkMode ? const Color(0xFF0C0C0F) : const Color(0xFFFFFFFF);
final Color surfaceContainerLow =
isDarkMode ? const Color(0xFF1F1F24) : const Color(0xFFF5F7FA);
final Color surfaceContainer =
isDarkMode ? const Color(0xFF24252A) : const Color(0xFFF0F2F5);
final Color surfaceContainerHigh =
isDarkMode ? const Color(0xFF2B2C31) : const Color(0xFFE9EBF0);
final Color surfaceContainerHighest =
isDarkMode ? const Color(0xFF323338) : const Color(0xFFE1E3E8);
// ====================== 边框 / 轮廓(柔和、不刺眼)======================
final Color outline =
isDarkMode ? Colors.grey.shade600 : Colors.grey.shade400;
final Color outlineVariant =
isDarkMode ? Colors.grey.shade700 : Colors.grey.shade300;
// ====================== 反色 / 遮罩 / 阴影(系统标准)======================
final Color inverseSurface =
isDarkMode ? const Color(0xFFE1E1E1) : const Color(0xFF2A2A2F);
final Color onInverseSurface =
isDarkMode ? const Color(0xFF1B1A1F) : const Color(0xFFFFFFFF);
final Color inversePrimary =
isDarkMode ? primarySwatch.shade400 : primarySwatch.shade200;
final Color shadow = Colors.black;
final Color scrim = Colors.black.withOpacity(0.6);
final Color surfaceTint = primary;
// ====================== 返回完整 ColorScheme ======================
return ColorScheme(
// ========== 基础亮度设置 ==========
brightness: isDarkMode ? Brightness.dark : Brightness.light,
// 作用:告知 Flutter 当前是亮色还是暗色模式
// 影响:MediaQuery.platformBrightness、StatusBar 图标颜色等系统级渲染
// ========== 主色系列(Primary)- 品牌主色调 ==========
/// 主色 - 最重要的品牌色
/// 使用组件:
/// - ElevatedButton(填充按钮背景)
/// - FloatingActionButton(FAB 背景,如果未设置专门主题)
/// - ProgressIndicator(进度条颜色)
/// - Switch 开启状态(滑块颜色)
/// - Checkbox 选中状态(填充色)
/// - Radio 选中状态(圆点颜色)
/// - Slider 激活轨道(已滑过部分)
/// - TabBar 指示器(下划线)
/// - NavigationBar 选中图标(如果未覆盖)
/// - 链接文字、可点击文字
/// - AppBar 操作按钮(如果 foregroundColor 未设置)
primary: primary,
/// 主色上的文字/图标颜色 - 确保在主色背景上可读
/// 使用组件:
/// - ElevatedButton 上的文字
/// - FilledButton 上的文字
/// - FloatingActionButton 上的图标
/// - SnackBar 操作按钮文字(如果未使用 inverse)
/// - 任何 primary 色背景上的内容
onPrimary: onPrimary,
/// 主色容器 - 比主色更柔和的背景
/// 使用组件:
/// - FloatingActionButton(M3 默认使用,比旧版 primary 更柔和)
/// - Chip(选中状态的背景)
/// - 需要强调但不刺眼的区域背景
/// - 关键数据卡片、重要信息容器
primaryContainer: primaryContainer,
/// 主色容器上的文字/图标颜色
/// 使用组件:
/// - FloatingActionButton 上的图标(M3 样式)
/// - Chip 选中状态下的文字
/// - primaryContainer 背景上的所有内容
onPrimaryContainer: onPrimaryContainer,
// ========== 辅助色系列(Secondary)- 次要操作 ==========
/// 辅助色 - 用于次要操作和组件
/// 使用组件:
/// - OutlinedButton 文字和边框(如果未专门设置)
/// - TextButton 文字颜色(如果未专门设置)
/// - ToggleButtons 选中状态
/// - FilterChip、ChoiceChip 选中状态
/// - 次要操作按钮、备选操作
/// - 标签、分类标识
secondary: secondary,
/// 辅助色上的文字/图标颜色
/// 使用组件:
/// - 任何 secondary 色背景上的文字和图标
onSecondary: onSecondary,
/// 辅助色容器 - 柔和的背景色
/// 使用组件:
/// - NavigationBar 选中指示器背景(那个圆形高亮)
/// - NavigationRail 选中指示器
/// - Chip 默认背景(未选中状态)
/// - 次要信息卡片、标签容器
/// - 筛选器、分类标签背景
secondaryContainer: secondaryContainer,
/// 辅助色容器上的文字/图标颜色
/// 使用组件:
/// - NavigationBar 选中项的图标和文字
/// - NavigationRail 选中项的图标和文字
/// - Chip 上的文字
/// - secondaryContainer 背景上的所有内容
onSecondaryContainer: onSecondaryContainer,
// ========== 点缀色系列(Tertiary)- 对比平衡 ==========
/// 点缀色 - 用于需要对比强调的元素
/// 使用组件:
/// - 特殊强调按钮(不同于主次操作)
/// - 促销标签、限时活动标识
/// - 新功能引导标记
/// - 与主色形成对比的强调元素
/// - 第三方登录按钮等特殊操作
tertiary: tertiary,
/// 点缀色上的文字/图标颜色
/// 使用组件:
/// - 任何 tertiary 色背景上的内容
onTertiary: onTertiary,
/// 点缀色容器 - 柔和的背景
/// 使用组件:
/// - 特殊标签背景
/// - 促销卡片背景
/// - 新功能提示框
/// - 需要与主色、次色区分的强调容器
tertiaryContainer: tertiaryContainer,
/// 点缀色容器上的文字/图标颜色
/// 使用组件:
/// - tertiaryContainer 背景上的所有内容
onTertiaryContainer: onTertiaryContainer,
// ========== 错误色系列(Error)- 警告和错误状态 ==========
/// 错误色 - 表示错误、删除、警告
/// 使用组件:
/// - TextField 错误状态下的边框和图标
/// - Form 验证错误提示
/// - 删除按钮、危险操作
/// - 错误提示图标
/// - 必填项标记(红色星号)
/// - 系统错误状态指示
error: error,
/// 错误色上的文字/图标颜色(通常是白色)
/// 使用组件:
/// - 错误按钮上的文字
/// - 错误图标上的内容
onError: onError,
/// 错误色容器 - 柔和的错误背景
/// 使用组件:
/// - 错误提示卡片背景
/// - 删除确认对话框背景
/// - 表单错误信息区域背景
/// - 警告提示框背景
errorContainer: errorContainer,
/// 错误色容器上的文字颜色
/// 使用组件:
/// - 错误提示卡片内的文字
/// - errorContainer 背景上的所有内容
onErrorContainer: onErrorContainer,
// ========== 背景色系列(Background)- 页面级背景 ==========
/// 页面背景色 - 最底层的背景
/// 使用组件:
/// - Scaffold 背景(如果 scaffoldBackgroundColor 未设置)
/// - 页面底层背景
/// - 大面积区域背景
/// 注意:实际 Scaffold 通常使用 scaffoldBackgroundColor,需要单独设置
background: background,
/// 背景上的文字/图标颜色
/// 使用组件:
/// - 页面上的主要文字
/// - 背景色上的图标
/// - 与 background 形成对比的内容
onBackground: onBackground,
// ========== 表面色系列(Surface)- 组件级背景 ==========
/// 表面色 - 组件的默认背景
/// 使用组件:
/// - Card 默认背景(如果 cardColor 未设置)
/// - Dialog 背景(如果 dialogBackgroundColor 未设置)
/// - BottomSheet 背景
/// - Drawer 背景(配合 canvasColor)
/// - 任何需要浮于背景之上的表面
/// 注意:实际组件可能读取专门的主题属性,需要同时设置
surface: surface,
/// 表面上的文字/图标颜色 - 主要内容色
/// 使用组件:
/// - Card 内的标题和正文
/// - Dialog 内的文字
/// - ListTile 标题和副标题
/// - 表面色背景上的主要内容
onSurface: onSurface,
/// 表面变体色 - 用于区分层级
/// 使用组件:
/// - 旧版组件的背景区分
/// - 需要与 surface 微妙区分的区域
/// - 某些 M2 组件的替代背景
/// 注意:M3 中逐渐被 surfaceContainer 系列取代
surfaceVariant: surfaceVariant,
/// 表面变体上的文字颜色 - 次要内容
/// 使用组件:
/// - 辅助文字、说明文字
/// - 图标(未激活状态)
/// - 占位符文字、提示信息
/// - 比 onSurface 优先级低的内容
onSurfaceVariant: onSurfaceVariant,
/// 暗表面色 - 最暗的表面层级
/// 使用组件:
/// - 底部区域、底层卡片
/// - 需要下沉视觉层级的区域
/// - 深色模式下的某些背景
surfaceDim: surfaceDim,
/// 亮表面色 - 最亮的表面层级
/// 使用组件:
/// - 顶部区域、主要卡片
/// - 需要提升视觉层级的区域
/// - 亮色模式下的主要表面
surfaceBright: surfaceBright,
/// 表面容器最低层 - 最底层的容器
/// 使用组件:
/// - 页面最底层容器(如底部导航栏下方)
/// - 与背景融合的区域
/// - 最低层级的卡片堆叠
surfaceContainerLowest: surfaceContainerLowest,
/// 表面容器低层 - 较低层级
/// 使用组件:
/// - 底部导航栏背景(NavigationBar)
/// - 较低层级的卡片
/// - 次要信息区域
surfaceContainerLow: surfaceContainerLow,
/// 表面容器默认层 - 标准容器背景
/// 使用组件:
/// - Card 背景(M3 推荐)
/// - 标准对话框背景
/// - 主要内容卡片
/// - 中等层级的容器
surfaceContainer: surfaceContainer,
/// 表面容器高层 - 较高层级
/// 使用组件:
/// - 悬浮卡片、展开面板
/// - 侧边栏、导航抽屉
/// - 需要突出显示的区域
surfaceContainerHigh: surfaceContainerHigh,
/// 表面容器最高层 - 最顶层
/// 使用组件:
/// - 顶部应用栏(AppBar 备选)
/// - 模态对话框、警告框
/// - 最高层级的弹出内容
/// - 需要最强分离感的区域
surfaceContainerHighest: surfaceContainerHighest,
// ========== 轮廓色系列(Outline)- 边框和分隔 ==========
/// 轮廓色 - 主要边框颜色
/// 使用组件:
/// - OutlinedButton 边框
/// - TextField 未聚焦边框
/// - Card 边框(如果设置)
/// - Divider 分割线(如果 dividerColor 未设置)
/// - 组件边界线、分隔线
outline: outline,
/// 轮廓变体色 - 次要边框,更柔和
/// 使用组件:
/// - 细分割线
/// - 弱化边框
/// - 禁用状态的边框
/// - 不明显的分隔线
outlineVariant: outlineVariant,
// ========== 效果色系列(Effects)- 阴影和遮罩 ==========
/// 阴影色 - 组件阴影
/// 使用组件:
/// - Card 阴影(有 elevation 时)
/// - AppBar 阴影
/// - FloatingActionButton 阴影
/// - 任何带 elevation 组件的阴影
/// 注意:通常是半透明黑色,而非纯黑
shadow: shadow,
/// 遮罩色 - 模态背后的遮罩
/// 使用组件:
/// - Dialog 背后的半透明层
/// - BottomSheet 展开时的背景遮罩
/// - Modal 遮罩
/// - 引导页遮罩、高亮遮罩
scrim: scrim,
// ========== 反色系列(Inverse)- 深色背景上的亮色 ==========
/// 反表面色 - 用于深色组件的亮色背景
/// 使用组件:
/// - SnackBar 背景(默认使用,在深色背景上显示亮色)
/// - 底部出现的提示条
/// - 临时通知、轻量提示
/// - 需要与页面形成强烈对比的组件
inverseSurface: inverseSurface,
/// 反表面上的文字颜色 - 深色文字
/// 使用组件:
/// - SnackBar 内的文字
/// - 任何 inverseSurface 背景上的内容
onInverseSurface: onInverseSurface,
/// 反主色 - 深色背景上的主色调
/// 使用组件:
/// - SnackBar 操作按钮文字("撤销"等)
/// - inverseSurface 背景上的强调色
/// - 深色卡片上的链接、按钮
inversePrimary: inversePrimary,
// ========== 表面着色(Surface Tint)- M3 特性 ==========
/// 表面着色 - 给 elevation 添加品牌色调
/// 使用组件:
/// - 所有带 elevation 的 M3 组件
/// - Card 阴影着色(elevation > 0 时)
/// - AppBar 滚动时的颜色变化(scrolledUnderElevation)
/// - Dialog、BottomSheet 的阴影着色
/// - 让灰色阴影带有品牌色调,增强品牌感
/// 原理:将主色混合到阴影中,elevation 越高着色越明显
surfaceTint: surfaceTint,
// ========== 固定色系列(Fixed)- 不随主题变化的品牌色 ==========
/// 固定主色容器 - 始终保持柔和主色
/// 使用组件:
/// - 品牌标识区域
/// - 需要保持品牌识别度的固定元素
/// - 广告横幅、品牌展示区
/// - 不随明暗模式变化的关键品牌元素
/// 特点:在明暗模式下保持相同的视觉感受
primaryFixed: primaryContainer,
/// 暗固定主色 - 较深的固定主色
/// 使用组件:
/// - 固定品牌元素的深色变体
/// - 需要层次感的固定品牌区域
primaryFixedDim: primary,
/// 固定主色上的文字
/// 使用组件:
/// - primaryFixed 背景上的内容
onPrimaryFixed: onPrimaryContainer,
/// 固定主色变体文字
/// 使用组件:
/// - primaryFixed 上的次要文字
onPrimaryFixedVariant: onPrimary,
/// 固定辅助色容器
/// 使用组件:
/// - 次要品牌元素的固定背景
/// - 辅助系统标识
secondaryFixed: secondaryContainer,
/// 暗固定辅助色
secondaryFixedDim: secondary,
/// 固定辅助色上的文字
onSecondaryFixed: onSecondaryContainer,
/// 固定辅助色变体文字
onSecondaryFixedVariant: onSecondary,
/// 固定点缀色容器
/// 使用组件:
/// - 点缀品牌元素的固定背景
tertiaryFixed: tertiaryContainer,
/// 暗固定点缀色
tertiaryFixedDim: tertiary,
/// 固定点缀色上的文字
onTertiaryFixed: onTertiaryContainer,
/// 固定点缀色变体文字
onTertiaryFixedVariant: onTertiary,
);
}
// ========== 构建文字主题(集成 ColorScheme)==========
TextTheme _buildTextTheme(bool isDarkMode, ColorScheme cs) {
// 基础样式,你可以替换为你的 TextStyles
final baseStyle = TextStyle(
color: cs.onSurface,
fontFamily: 'Roboto', // 或你的字体
);
return TextTheme(
// Display
displayLarge: baseStyle.copyWith(
fontSize: 57,
fontWeight: FontWeight.w400,
letterSpacing: -0.25,
color: cs.onSurface),
displayMedium: baseStyle.copyWith(
fontSize: 45,
fontWeight: FontWeight.w400,
letterSpacing: 0,
color: cs.onSurface),
displaySmall: baseStyle.copyWith(
fontSize: 36,
fontWeight: FontWeight.w400,
letterSpacing: 0,
color: cs.onSurface),
// Headline
headlineLarge: baseStyle.copyWith(
fontSize: 32,
fontWeight: FontWeight.w400,
letterSpacing: 0,
color: cs.onSurface),
headlineMedium: baseStyle.copyWith(
fontSize: 28,
fontWeight: FontWeight.w400,
letterSpacing: 0,
color: cs.onSurface),
headlineSmall: baseStyle.copyWith(
fontSize: 24,
fontWeight: FontWeight.w400,
letterSpacing: 0,
color: cs.onSurface),
// Title
titleLarge: baseStyle.copyWith(
fontSize: 22,
fontWeight: FontWeight.w400,
letterSpacing: 0,
color: cs.onSurface),
titleMedium: baseStyle.copyWith(
fontSize: 16,
fontWeight: FontWeight.w500,
letterSpacing: 0.15,
color: cs.onSurface),
titleSmall: baseStyle.copyWith(
fontSize: 14,
fontWeight: FontWeight.w500,
letterSpacing: 0.1,
color: cs.onSurface),
// Body
bodyLarge: baseStyle.copyWith(
fontSize: 16,
fontWeight: FontWeight.w400,
letterSpacing: 0.5,
color: cs.onSurface),
bodyMedium: baseStyle.copyWith(
fontSize: 14,
fontWeight: FontWeight.w400,
letterSpacing: 0.25,
color: cs.onSurface),
bodySmall: baseStyle.copyWith(
fontSize: 12,
fontWeight: FontWeight.w400,
letterSpacing: 0.4,
color: cs.onSurfaceVariant),
// Label
labelLarge: baseStyle.copyWith(
fontSize: 14,
fontWeight: FontWeight.w500,
letterSpacing: 0.1,
color: cs.primary),
labelMedium: baseStyle.copyWith(
fontSize: 12,
fontWeight: FontWeight.w500,
letterSpacing: 0.5,
color: cs.onSurfaceVariant),
labelSmall: baseStyle.copyWith(
fontSize: 11,
fontWeight: FontWeight.w500,
letterSpacing: 0.5,
color: cs.onSurfaceVariant),
);
}
}
更多推荐
所有评论(0)