主题
Material 3 设计系统规范
最后更新:2026-02-21 版本:v1.2 状态:✅ 强制执行
本文档定义 Reading Vocab Helper 项目的 Material 3 设计系统,包括文本、颜色、图标、间距等基础规范和设计原则。
如需查看具体组件的详细实战指南,请参考:
📋 目录
文本样式最佳实践
核心原则:简洁优先 + 主题优先
核心理念
本章节定义 文本样式(fontSize、fontWeight、textStyle) 的使用原则:
- ✅ 90%的文本使用默认样式(bodyMedium = 14sp, w400, onSurface)
- ✅ 只在需要时设置 fontSize/fontWeight(建立视觉层级)
- ✅ 颜色使用 colorScheme(详见颜色系统规范章节)
- ✅ 避免硬编码(Colors.blue → colorScheme.primary)
三级简化策略
Level 0:不设置 fontSize(优先考虑,~87%场景)
dart
Text('这是普通正文')
Text('列表项描述')
Text('卡片内容')- ✅ 默认效果:14sp, w400, onSurface 色(= bodyMedium)
- ✅ 适用场景:普通正文、列表项内容、卡片描述、对话框内容
- ✅ 何时使用:标准大小、正常粗细的所有文本
Level 1:只设置必要的样式(常见,~45%场景)
dart
// 只改颜色(使用主题颜色,推荐)✅
Text('提示', style: TextStyle(
color: Theme.of(context).colorScheme.onSurfaceVariant
))
Text('强调', style: TextStyle(
color: Theme.of(context).colorScheme.tertiary
))
// 只改粗细
Text('加粗', style: TextStyle(fontWeight: FontWeight.w600))
// 同时改颜色和粗细
Text('重要提示', style: TextStyle(
color: Theme.of(context).colorScheme.error,
fontWeight: FontWeight.w600,
))- ✅ 适用场景:普通大小,但需要特殊颜色或粗细
- ✅ 推荐:使用主题 colorScheme 而非硬编码颜色
- ❌ 避免冗余:
bodyMedium.copyWith(color: xxx)→TextStyle(color: xxx)
Level 2:使用语义样式(建立视觉层级,2%场景)
dart
// 大标题
Text('页面标题', style: context.headlineSmall)
// 节标签(或用 SectionLabel 组件)
SectionLabel('Definition')
// 按钮文字
Text('开始复习', style: context.bodyLarge)
// 小号说明
Text('次要信息', style: context.bodySmall)- ✅ Extension Methods 简化:
context.bodyLarge替代Theme.of(context).textTheme.bodyLarge - ✅ 语义组件封装:
SectionLabel('标签')用于节标题 - ✅ 适用场景:需要不同大小来建立视觉层级
Level 3:完全自定义(罕见场景,<1%)
dart
Text('特殊需求', style: context.bodyMedium.copyWith(
color: Colors.blue,
fontWeight: FontWeight.w600,
))决策树:是否需要设置样式?
开始写 Text()
↓
是否需要不同的大小?
├─ 否(普通大小14sp)→ 是否需要特殊颜色/粗细?
│ ├─ 否 → ✅ 不写 style(默认 bodyMedium)
│ └─ 是 → ✅ TextStyle(color: colorScheme.xxx)
│ 或 TextStyle(fontWeight: xxx)
│
└─ 是(需要更大/更小)→ ✅ 使用语义样式
├─ 大标题 → context.headlineSmall
├─ 按钮文字 → context.bodyLarge
└─ 小号说明 → context.bodySmall关键原则:
- 默认优先 - 普通大小、黑色、正常粗细 → 不写 style
- 主题颜色 - 使用 colorScheme.tertiary 而非 Colors.blue
- 语义大小 - 使用 headlineSmall 而非 fontSize: 18
常见模式快速参考
| 文本类型 | 推荐做法 | 理由 |
|---|---|---|
| 普通正文 | Text('内容') | 默认 bodyMedium |
| 次要文本 | Text('提示', style: TextStyle(color: colorScheme.onSurfaceVariant)) | 使用主题色 ✅ |
| 强调文本 | Text('强调', style: TextStyle(color: colorScheme.tertiary)) | 使用主题色 ✅ |
| 页面标题 | Text('标题', style: context.headlineSmall) | 语义大小 |
| 节标签 | SectionLabel('标签') | 统一组件 |
| 按钮文字 | Text('按钮', style: context.bodyLarge) | 稍大易点 |
| 小号说明 | Text('说明', style: context.bodySmall) | 12sp 次要 |
| 错误提示 | Text('错误', style: TextStyle(color: colorScheme.error)) | 使用主题色 ✅ |
| 动态强调 | copyWith(fontWeight: isSelected ? w600 : w400) | 状态切换 |
避免过度设计 ❌
dart
// ❌ 错误:冗余的 bodyMedium(fontSize 重复)
Text('内容', style: Theme.of(context).textTheme.bodyMedium)
// ✅ 正确:直接使用默认
Text('内容')
// ❌ 错误:只改颜色却引用整个 bodyMedium
Text('提示', style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
))
// ✅ 正确:只写需要的
Text('提示', style: TextStyle(
color: Theme.of(context).colorScheme.onSurfaceVariant,
))
// ⚠️ 不推荐:硬编码颜色(不跟随主题)
Text('提示', style: TextStyle(color: Colors.grey))
// ✅ 推荐:使用主题颜色(自动适配浅色/深色模式)
Text('提示', style: TextStyle(
color: Theme.of(context).colorScheme.onSurfaceVariant,
))主题颜色 vs 硬编码颜色 ⭐ 重要
使用主题颜色(推荐):
dart
// ✅ 语义清晰、主题适配、全局一致
color: Theme.of(context).colorScheme.tertiary // 第三强调色
color: Theme.of(context).colorScheme.onSurfaceVariant // 次要文本色
color: Theme.of(context).colorScheme.error // 错误色硬编码颜色(需谨慎):
dart
// ❌ 不跟随主题、可能在深色模式下不可见
color: Colors.blue
color: Colors.grey.shade600
color: Color(0xFF123456)何时可以使用硬编码颜色:
- 品牌色(如公司 Logo)
- 特殊视觉效果(如渐变、动画)
- 临时调试
其他情况都应使用主题颜色!
工具支持
- Extension Methods:
lib/core/extensions/text_style_extensions.dart- 简化语法:
context.bodyLarge替代Theme.of(context).textTheme.bodyLarge
- 简化语法:
- 语义组件:
lib/shared/presentation/widgets/text/section_label.dartSectionLabel('Definition')专用于节标题
最佳实践总结
fontSize 优先级:
- 🥇 不设置(默认 bodyMedium = 14sp)→ 87%场景
- 🥈 使用语义样式(headlineSmall, bodyLarge)→ 13%场景
- 🚫 避免硬编码(fontSize: 18)
颜色优先级:
- 🥇 不设置(默认 onSurface = 黑色)→ 55%场景
- 🥈 使用主题颜色(colorScheme.tertiary)→ 推荐!
- 🚫 避免硬编码(Colors.blue)→ 需谨慎
fontWeight 优先级:
- 🥇 不设置(默认 w400 或 w600)→ 78%场景
- 🥈 有意修改(加粗/加重)→ 22%场景
核心原则:先不写 style,运行看效果,需要调整时:
- 改大小 → 用语义样式(context.headlineSmall)
- 改颜色 → 用主题颜色(colorScheme.tertiary)
- 改粗细 → 用 TextStyle(fontWeight: xxx)
保持简洁,遵循主题!
字体尺寸和字重对应表 🆕
Material 3 标准 fontSize
| TextStyle | fontSize | 典型用途 | 示例 |
|---|---|---|---|
displayLarge | 57sp | 超大展示文字 | 启动页标题 |
displayMedium | 45sp | 大展示文字 | 空状态标题 |
displaySmall | 36sp | 小展示文字 | 页面大标题 |
headlineLarge | 32sp | 大标题 | 章节标题 |
headlineMedium | 28sp | 中标题 | 详情页标题 |
headlineSmall | 24sp | 小标题 | 卡片标题 |
titleLarge | 22sp | 大标题 | AppBar 标题 |
titleMedium | 16sp | 中标题 | ListTile 标题 |
titleSmall | 14sp | 小标题 | Tab 标签 |
bodyLarge | 16sp | 大正文 | 主要内容 |
bodyMedium | 14sp | 中正文(默认) | 普通文本 |
bodySmall | 12sp | 小正文 | 辅助说明 |
labelLarge | 14sp | 大标签 | 按钮文字 |
labelMedium | 12sp | 中标签 | 导航标签 |
labelSmall | 11sp | 小标签 | 徽章文字 |
Material 3 标准 fontWeight
| FontWeight | 数值 | 用途 | 示例 |
|---|---|---|---|
FontWeight.w100 | Thin | 极细(少用) | 装饰文字 |
FontWeight.w200 | ExtraLight | 超轻(少用) | 装饰文字 |
FontWeight.w300 | Light | 轻 | 次要说明 |
FontWeight.w400 | Regular(默认) | 正文 | bodyMedium |
FontWeight.w500 | Medium | 中等 | 强调文字 |
FontWeight.w600 | SemiBold | 半粗 | 标题 |
FontWeight.w700 | Bold | 粗体 | 重要标题 |
FontWeight.w800 | ExtraBold | 超粗(少用) | 特殊强调 |
FontWeight.w900 | Black | 黑体(少用) | 特殊设计 |
常用组合速查
| 场景 | fontSize | fontWeight | 示例代码 |
|---|---|---|---|
| 页面大标题 | 24sp | w600 | headlineSmall |
| 卡片标题 | 16sp | w500 | titleMedium |
| 按钮文字 | 14sp | w500 | labelLarge |
| 普通正文 | 14sp | w400 | bodyMedium(默认) |
| 辅助说明 | 12sp | w400 | bodySmall |
| 强调文字 | 14sp | w600 | bodyMedium + w600 |
| 错误提示 | 12sp | w400 | bodySmall + error 色 |
代码示例
dart
// ✅ 使用语义化样式(推荐)
Text('标题', style: context.headlineSmall)
Text('正文', style: context.bodyMedium)
Text('说明', style: context.bodySmall)
// ✅ 需要修改时,最小化设置
Text('强调', style: TextStyle(fontWeight: FontWeight.w600))
Text('错误', style: TextStyle(color: colorScheme.error))
// ❌ 避免硬编码完整样式
Text('标题', style: TextStyle(fontSize: 24, fontWeight: FontWeight.w600))图标使用规范
核心原则:使用语义化颜色 + 避免自定义背景
图标颜色使用规范
| 图标类型 | 推荐颜色 | 使用场景 | 默认行为 |
|---|---|---|---|
| 主要操作图标 | primary | FAB、主按钮图标 | ⚠️ 需手动设置 |
| 导航图标 | onSurface | AppBar、BottomNavigationBar | ✅ 部分自动 |
| 列表项辅助图标 | onSurfaceVariant ⭐ | Settings、ListTile | ✅ ListTile 自动 |
| 次要图标 | onSurfaceVariant | 提示、说明 | ⚠️ 需手动设置 |
| 强调图标 | primary | 选中状态、CTA | ⚠️ 需手动设置 |
| 错误/警告图标 | error | 错误提示 | ⚠️ 需手动设置 |
Widget 默认颜色行为
✅ 自动使用正确颜色的 Widget:
dart
// IconButton 默认 onSurfaceVariant
IconButton(
icon: Icon(Icons.settings), // ✅ 自动灰色
onPressed: () {},
)
// ListTile 默认 onSurfaceVariant
ListTile(
leading: Icon(Icons.settings), // ✅ 自动灰色
title: Text('Settings'),
)⚠️ 需要手动设置颜色的场景:
dart
// 裸 Icon 默认 onSurface(太深)
Icon(Icons.info) // ❌ 黑色,应手动设置
// 自定义布局中的图标
Row(
children: [
Icon(Icons.star, color: colorScheme.onSurfaceVariant), // ✅
Text('收藏'),
],
)避免过度设计 ❌
❌ 错误:iOS 风格的图标背景
dart
// Settings 页面当前问题(已识别需修复)
Container(
decoration: BoxDecoration(
color: colorScheme.onSurface, // ❌ 黑色背景
shape: BoxShape.circle,
),
child: Icon(icon, color: Colors.white), // ❌ 硬编码白色
)✅ 正确:直接使用主题色图标
dart
// 方案1:最简洁(推荐)
Icon(icon, color: colorScheme.onSurfaceVariant)
// 方案2:如需背景,使用主题色容器
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(8), // 圆角矩形
),
child: Icon(icon, color: colorScheme.onPrimaryContainer),
)特殊场景例外
合理使用硬编码颜色的场景:
- 相机/全屏查看器:黑色背景 → 白色图标
- 数据可视化:图表图例色块
- 品牌元素:Logo、特定品牌色
其他场景都应使用主题颜色!
快速决策树
需要显示图标
↓
使用 IconButton 或 ListTile?
├─ 是 → ✅ 不设置 color(自动正确)
└─ 否 → 这是什么类型的图标?
├─ 主要操作 → color: colorScheme.primary
├─ 导航图标 → color: colorScheme.onSurface
├─ 辅助图标 → color: colorScheme.onSurfaceVariant
└─ 错误提示 → color: colorScheme.error常见模式快速参考
| 场景 | 推荐代码 |
|---|---|
| FAB 图标 | Icon(Icons.add, color: colorScheme.onPrimary) |
| AppBar 返回 | Icon(Icons.arrow_back) 或 IconButton(...) |
| Settings 列表项 | Icon(Icons.settings, color: colorScheme.onSurfaceVariant) |
| 错误提示 | Icon(Icons.error, color: colorScheme.error) |
| 成功提示 | Icon(Icons.check_circle, color: Colors.green) ⚠️ |
参考资料:
保持简洁,遵循主题!
图标尺寸规范 🆕
标准图标尺寸
| 尺寸 | 数值 | 用途 |
|---|---|---|
| 超小 | 16dp | 下拉箭头、Badge |
| 小 | 18dp | Chip、密集列表 |
| 按钮内 | 20dp | Button.icon |
| 标准 | 24dp | IconButton、AppBar、ListTile |
| 中等 | 28dp | 略大图标 |
| 大 | 32dp | 底部导航 |
| 超大 | 36dp | 强调图标、大 FAB |
| 特大 | 40dp | 主要 CTA |
| 巨大 | 48dp | 空状态主图标 |
组件默认图标大小
| 组件 | 默认大小 |
|---|---|
| IconButton | 24dp |
| FilledButton.icon | 18dp |
| FAB | 24dp |
| FAB.large | 36dp |
| BottomNavigationBar | 24dp |
| ListTile | 24dp |
| AppBar | 24dp |
| Chip | 18dp |
详细规范
完整的图标尺寸规范请查看:icon-size-guide.md
颜色系统规范
核心原则:永远使用 colorScheme,避免硬编码颜色
颜色角色系统
Material 3 采用语义化颜色角色系统,每个颜色都有明确的用途:
| 颜色角色 | 用途 | 典型场景 | 示例 |
|---|---|---|---|
primary | 主要操作和强调 | FAB、主要按钮、选中状态 | colorScheme.primary |
onPrimary | primary 上的文本/图标 | 白色文本在蓝色按钮上 | colorScheme.onPrimary |
secondary | 次要操作 | 次要按钮、筛选器 | colorScheme.secondary |
tertiary | 补充强调 | 标签、小图标、辅助操作 | colorScheme.tertiary |
error | 错误状态 | 错误提示、删除按钮 | colorScheme.error |
surface | 卡片/容器背景 | Card、BottomSheet、Dialog | colorScheme.surface |
onSurface | surface 上的主要文本 | 标题、正文 | colorScheme.onSurface |
onSurfaceVariant | surface 上的次要文本 | 说明文字、图标、边框 | colorScheme.onSurfaceVariant |
outline | 边框和分隔线 | TextField 边框、Divider | colorScheme.outline |
surfaceContainerHighest | 最高层级容器 | 输入框背景、菜单背景 | colorScheme.surfaceContainerHighest |
决策树:如何选择颜色
需要设置颜色?
├─ 文本/图标
│ ├─ 主要内容(标题、正文) → onSurface(默认,通常不设置)
│ ├─ 次要内容(说明、辅助) → onSurfaceVariant
│ ├─ 主要操作 → primary
│ ├─ 错误提示 → error
│ └─ 禁用状态 → onSurface.withOpacity(0.38)
│
├─ 背景/容器
│ ├─ 页面背景 → scaffoldBackgroundColor(默认)
│ ├─ 卡片/弹窗 → surface
│ ├─ 选中容器 → primaryContainer
│ ├─ 输入框背景 → surfaceContainerHighest
│ └─ 错误容器 → errorContainer
│
└─ 边框/分隔线
├─ 标准边框 → outline
├─ 输入框边框 → outline(未聚焦)/ primary(聚焦)
└─ 分隔线 → outline.withOpacity(0.12)三级规范
Level 1:100%主题色(推荐,~85%场景) ✅
dart
// ✅ 正确:使用 colorScheme
Container(
color: colorScheme.surface,
child: Text(
'Title',
style: TextStyle(color: colorScheme.onSurface),
),
)
Icon(Icons.info, color: colorScheme.primary)Level 2:主题色 + 透明度调整(谨慎使用,~13%场景) ⚠️
dart
// ⚠️ 可接受:基于主题色调整透明度
Container(
color: colorScheme.primary.withOpacity(0.1), // 淡背景
)
Text(
'Disabled',
style: TextStyle(color: colorScheme.onSurface.withOpacity(0.38)),
)Level 3:硬编码颜色(禁止,只在极特殊情况) ❌
dart
// ❌ 错误:硬编码颜色
Container(color: Colors.blue) // 不适配深色模式
Text('Error', style: TextStyle(color: Color(0xFFFF0000))) // 不语义化常见场景快速参考
| 场景 | 推荐颜色 | 示例代码 |
|---|---|---|
| 卡片背景 | surface | Container(color: colorScheme.surface) |
| 卡片标题 | onSurface(默认) | Text('Title') 或 TextStyle(color: colorScheme.onSurface) |
| 卡片说明文字 | onSurfaceVariant | TextStyle(color: colorScheme.onSurfaceVariant) |
| 主要按钮 | primary | ElevatedButton(自动使用) |
| 次要按钮 | secondary | FilledButton.tonal(自动使用) |
| 文本按钮 | primary | TextButton(自动使用) |
| 错误提示 | error | TextStyle(color: colorScheme.error) |
| 成功提示 | tertiary 或 primary | TextStyle(color: colorScheme.tertiary) |
| 边框 | outline | BorderSide(color: colorScheme.outline) |
| 分隔线 | outline.withOpacity(0.12) | Divider(color: colorScheme.outline.withOpacity(0.12)) |
| 选中状态背景 | primaryContainer | Container(color: colorScheme.primaryContainer) |
| 选中状态文字 | onPrimaryContainer | TextStyle(color: colorScheme.onPrimaryContainer) |
⚠️ 反模式和常见错误
dart
// ❌ 错误示例
Colors.blue // 硬编码,不适配深色模式
Color(0xFF2196F3) // 硬编码,不语义化
Colors.grey[600] // 不语义化
Theme.of(context).primaryColor // 已废弃,使用 colorScheme.primary
// ✅ 正确示例
colorScheme.primary // 语义化,自动适配主题
colorScheme.onSurfaceVariant // 语义化,自动适配主题
colorScheme.outline // 语义化,自动适配主题特殊场景例外
例外1:品牌色强制要求
dart
// 仅当品牌指南明确要求固定色值时使用
const brandBlue = Color(0xFF1976D2); // 必须在 theme_config.dart 中定义例外2:数据可视化图表
dart
// 图表需要多种区分色时,仍优先使用 colorScheme 扩展
final chartColors = [
colorScheme.primary,
colorScheme.secondary,
colorScheme.tertiary,
colorScheme.error, // 作为最后手段
];参考资料:
间距和布局规范
核心原则:使用 4dp 倍数,遵循视觉层次
标准间距系统
Material 3 采用4dp 基准网格系统:
| 间距名称 | 数值 | 用途 | 示例 |
|---|---|---|---|
xxs | 4dp | 紧密元素间距 | 图标与文字间距、内联标签间距 |
xs | 8dp | 相关元素间距 | 列表项内部元素、表单字段内部 |
sm | 12dp | 小组件间距 | 按钮组内部间距、小卡片内边距 |
md | 16dp | 标准间距(最常用) | 页面水平边距、卡片内边距、段落间距 |
lg | 24dp | 大组件间距 | Section 之间、大卡片间距 |
xl | 32dp | 页面级间距 | 页面顶部间距、大标题下方间距 |
xxl | 48dp | 超大间距 | 空状态图标与文字、启动页元素 |
决策树:如何选择间距
需要设置间距?
├─ 水平方向
│ ├─ 页面左右边距 → 16dp (md)
│ ├─ 卡片内容左右边距 → 16dp (md)
│ ├─ 列表项左右边距 → 16dp (md)
│ ├─ 图标与文字间距 → 8dp (xs) 或 12dp (sm)
│ └─ 按钮组内部间距 → 8dp (xs) 或 12dp (sm)
│
├─ 垂直方向
│ ├─ Section 标题上方 → 24dp (lg) 或 32dp (xl)
│ ├─ Section 标题下方 → 12dp (sm)
│ ├─ 卡片之间 → 16dp (md)
│ ├─ 列表项之间 → 12dp (sm) 或 16dp (md)
│ ├─ 段落之间 → 16dp (md)
│ └─ 页面顶部/底部 → 16dp (md) 或 24dp (lg)
│
└─ 内边距 (Padding)
├─ 卡片内边距 → 16dp (md)
├─ 按钮内边距 → 水平 16dp, 垂直 8dp
├─ Dialog 内边距 → 24dp (lg)
└─ BottomSheet 内边距 → 水平 16dp, 垂直 24dp项目标准间距常量
建议在项目中定义常量(可选,但推荐):
dart
// lib/config/spacing_config.dart
class Spacing {
static const double xxs = 4;
static const double xs = 8;
static const double sm = 12;
static const double md = 16; // 最常用
static const double lg = 24;
static const double xl = 32;
static const double xxl = 48;
}
// 使用示例
Padding(
padding: EdgeInsets.all(Spacing.md), // 16dp
child: Column(
spacing: Spacing.sm, // 12dp (Flutter 3.16+)
children: [...],
),
)常见场景快速参考
| 场景 | 推荐间距 | 示例代码 |
|---|---|---|
| 页面水平边距 | 16dp | EdgeInsets.symmetric(horizontal: 16) |
| 页面顶部间距 | 16dp + statusBar | EdgeInsets.only(top: MediaQuery.of(context).padding.top + 16) |
| Card 内边距 | 16dp | EdgeInsets.all(16) |
| ListTile 内边距 | 水平16dp, 垂直12dp | EdgeInsets.symmetric(horizontal: 16, vertical: 12) |
| 图标与文字间距 | 8dp 或 12dp | SizedBox(width: 8) |
| Section 标题间距 | 上24dp, 下12dp | EdgeInsets.only(top: 24, bottom: 12) |
| 按钮组间距 | 8dp 或 12dp | Row(spacing: 8, children: [...]) |
| 卡片之间间距 | 16dp | SizedBox(height: 16) |
⚠️ 反模式和常见错误
dart
// ❌ 错误示例
Padding(padding: EdgeInsets.all(15)) // 非4倍数
SizedBox(height: 10) // 非4倍数
EdgeInsets.symmetric(horizontal: 20, vertical: 14) // 混用非标准值
// ✅ 正确示例
Padding(padding: EdgeInsets.all(16)) // 4倍数
SizedBox(height: 12) // 4倍数
EdgeInsets.symmetric(horizontal: 16, vertical: 12) // 标准值响应式间距(可选)
dart
// 大屏设备可适当增加间距
final horizontalPadding = MediaQuery.of(context).size.width > 600 ? 24.0 : 16.0;参考资料:
组件使用规范
核心原则:优先使用 Material 3 组件,让组件自动处理样式
常用组件规范
1. 按钮 (Buttons)
快速参考:
| 组件 | 用途 | 自动样式 | 示例 |
|---|---|---|---|
FilledButton | 主要操作(高强调) | primary 背景 + onPrimary 文字 | 保存、提交、确认 |
FilledButton.tonal | 次要操作(中强调) | secondaryContainer 背景 | 编辑、下一步 |
OutlinedButton | 备选操作(低强调) | 透明背景 + outline 边框 | 取消、返回 |
TextButton | 最低优先级操作 | 透明背景 + primary 文字 | 跳过、了解更多 |
IconButton | 工具栏操作 | 自动使用 onSurfaceVariant | 搜索、设置 |
FloatingActionButton | 主要浮动操作 | primary 背景 + onPrimary 图标 | 添加、拍照 |
📖 详细规范:查看 按钮样式规范
- 完整的代码示例和模板
- 详细的布局规范(高度/宽度/间距)
- 常见错误对比和修复方案
- 圆角统一规范(12px)
✅ 推荐用法(让组件自动处理颜色):
dart
FilledButton(
onPressed: () {},
child: Text('保存'), // 自动使用 onPrimary 颜色
)
FilledButton.tonal(
onPressed: () {},
child: Text('编辑'), // 自动使用 onSecondaryContainer 颜色
)
IconButton(
icon: Icon(Icons.search), // 自动使用 onSurfaceVariant 颜色
onPressed: () {},
)❌ 不推荐(手动设置颜色):
dart
// ❌ 多余的颜色设置
FilledButton(
onPressed: () {},
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(colorScheme.primary), // 多余
),
child: Text('保存', style: TextStyle(color: colorScheme.onPrimary)), // 多余
)2. 卡片和容器 (Cards & Containers)
| 组件 | 用途 | 自动样式 | 示例 |
|---|---|---|---|
Card | 内容容器 | surface 背景 + elevation + 圆角 | 列表项、详情卡片 |
Container | 自定义容器 | 需手动设置 color: colorScheme.surface | 自定义卡片 |
ListTile | 列表项 | 自动 padding + 点击效果 | 设置列表、导航列表 |
✅ 推荐用法:
dart
Card(
child: ListTile(
leading: Icon(Icons.book), // 自动使用 onSurfaceVariant
title: Text('书名'), // 自动使用 onSurface
subtitle: Text('100 words'), // 自动使用 onSurfaceVariant
),
)3. AppBar 和导航 (AppBar & Navigation)
| 组件 | 用途 | 自动样式 | 示例 |
|---|---|---|---|
AppBar | 标准顶栏 | surface 背景 + onSurface 文字 | 页面标题栏 |
BottomNavigationBar | 底部导航 | 自动使用 primary(选中)/ onSurfaceVariant(未选中) | 主导航 |
NavigationRail | 侧边导航 | 同上 | 平板侧边导航 |
✅ 推荐用法:
dart
AppBar(
title: Text('书籍列表'), // 自动使用 onSurface
actions: [
IconButton(
icon: Icon(Icons.search), // 自动使用 onSurface
onPressed: () {},
),
],
)4. 输入组件 (Input)
| 组件 | 用途 | 自动样式 | 注意事项 |
|---|---|---|---|
TextField | 文本输入 | outline 边框 + onSurface 文字 | 聚焦时边框自动变 primary |
Switch | 开关 | primary(开启)/ outline(关闭) | 需设置 activeColor 为 colorScheme.primary |
Checkbox | 复选框 | 同上 | 自动使用主题色 |
Radio | 单选框 | 同上 | 自动使用主题色 |
✅ 推荐用法:
dart
TextField(
decoration: InputDecoration(
labelText: '书名', // 自动使用 onSurfaceVariant
hintText: '请输入书名', // 自动使用 onSurfaceVariant
),
)
Switch(
value: value,
onChanged: (v) {},
activeColor: colorScheme.primary, // 需手动设置
)5. 对话框和弹窗 (Dialogs & Sheets)
快速参考:
| 组件 | 用途 | 自动样式 | 示例 |
|---|---|---|---|
AlertDialog | 警告对话框 | surface 背景 + 28dp 圆角 | 确认删除、错误提示 |
BottomSheet | 底部弹窗 | surface 背景 + 顶部圆角 | 分组选择、操作菜单 |
SnackBar | 底部提示条 | inverseSurface 背景 + onInverseSurface 文字 | 操作反馈 |
📖 详细规范:查看 对话框样式规范
- 完整的使用场景和示例
- 详细的代码模板
- 按钮布局规范
- 圆角统一规范(28px)
✅ 推荐用法:
dart
AlertDialog(
title: Text('确认删除'), // 自动使用 onSurface
content: Text('删除后无法恢复'), // 自动使用 onSurfaceVariant
actions: [
TextButton(
onPressed: () {},
child: Text('取消'),
),
FilledButton(
onPressed: () {},
child: Text('删除'),
),
],
)组件选择决策树
需要交互组件?
├─ 操作按钮
│ ├─ 主要操作(1个) → FilledButton
│ ├─ 次要操作(1-2个) → FilledButton.tonal
│ ├─ 取消/返回 → OutlinedButton 或 TextButton
│ └─ 工具栏操作 → IconButton
│
├─ 容器
│ ├─ 标准卡片 → Card(自动样式)
│ ├─ 列表项 → ListTile(自动样式)
│ └─ 自定义容器 → Container(color: colorScheme.surface)
│
├─ 输入
│ ├─ 文本输入 → TextField(自动样式)
│ ├─ 开关 → Switch(需设置 activeColor)
│ └─ 选择 → Checkbox / Radio(自动样式)
│
└─ 反馈
├─ 确认/警告 → AlertDialog
├─ 选择菜单 → BottomSheet
└─ 操作反馈 → SnackBar⚠️ 反模式和常见错误
dart
// ❌ 错误:手动构建按钮样式
Container(
decoration: BoxDecoration(
color: colorScheme.primary,
borderRadius: BorderRadius.circular(8),
),
child: InkWell(
onTap: () {},
child: Padding(
padding: EdgeInsets.all(16),
child: Text('按钮', style: TextStyle(color: colorScheme.onPrimary)),
),
),
)
// ✅ 正确:使用标准组件
FilledButton(
onPressed: () {},
child: Text('按钮'),
)
// ❌ 错误:手动设置已有的默认颜色
ListTile(
leading: Icon(Icons.book, color: colorScheme.onSurfaceVariant), // 多余
title: Text('书名', style: TextStyle(color: colorScheme.onSurface)), // 多余
)
// ✅ 正确:让组件自动处理
ListTile(
leading: Icon(Icons.book),
title: Text('书名'),
)参考资料:
Claude自动化执行机制
目标:让 Claude 自动遵循 Material 3 规范,并在用户提出非规范需求时给出建议
工作流程
用户提出需求
↓
Claude 分析需求类型
├─ 符合 Material 3 规范 → 直接实施
├─ 可用 Material 3 替代 → 提供建议 + 等待用户确认
└─ 必须自定义(品牌要求) → 提醒潜在问题 + 实施决策矩阵
| 用户需求 | Claude 响应 | 示例 |
|---|---|---|
| "按钮改成蓝色" | 🔄 提供 Material 3 建议 | "建议使用 FilledButton(自动使用主题 primary 色),而不是硬编码蓝色。这样可以自动适配深色模式。是否采用?" |
| "字体大小改成 15sp" | 🔄 提供语义化建议 | "Material 3 没有 15sp 标准尺寸。建议使用 bodyLarge(16sp)或 bodyMedium(14sp)。您希望用哪一个?" |
| "图标改成灰色" | 🔄 提供主题色建议 | "建议使用 colorScheme.onSurfaceVariant(自动适配主题),而不是硬编码灰色。是否采用?" |
| "卡片圆角改成 20dp" | ⚠️ 提醒后实施 | "Material 3 标准圆角为 12dp。如需自定义为 20dp,会与系统风格不一致。确认继续?" |
| "品牌要求主色必须是 #1976D2" | ✅ 引导到正确位置 | "了解。建议在 theme_config.dart 中统一定义,然后通过 colorScheme.primary 使用,而不是在代码中硬编码。这样便于维护。" |
| "删除按钮背景改成红色" | ✅ 符合规范(错误状态) | "使用 colorScheme.error 表示危险操作是 Material 3 推荐做法。✅" |
Claude 响应模板
模板 1:建议 Material 3 替代方案
⚠️ Material 3 规范建议
您的需求:【用户原始需求】
建议方案:【Material 3 标准方案】
理由:
- ✅ 自动适配深色模式
- ✅ 保持系统一致性
- ✅ 减少维护成本
是否采用建议方案?模板 2:自定义需求确认
⚠️ 自定义需求确认
您的需求:【用户原始需求】
影响:
- ⚠️ 与 Material 3 标准不一致(标准值:XXX)
- ⚠️ 可能在深色模式下效果不佳
- ⚠️ 增加维护成本
如需继续,建议:
- 定义在 theme_config.dart 而非硬编码
- 添加注释说明原因
是否确认继续?模板 3:引导到正确实现位置
✅ 需求合理,建议实施位置
您的需求:【用户原始需求】
推荐实施方式:
1. 在 `theme_config.dart` 中定义常量
2. 通过 `colorScheme` 或 `textTheme` 引用
3. 避免在组件中硬编码
这样做的好处:
- ✅ 集中管理,便于修改
- ✅ 保持一致性
- ✅ 易于测试和维护
是否采用此方式?自动检查清单(Claude 内部使用)
每次实施前,Claude 应自问:
- [ ] 是否使用了
colorScheme而非Colors.xxx? - [ ] 是否使用了
textTheme而非硬编码fontSize? - [ ] 是否使用了 Material 3 标准组件而非手动构建?
- [ ] 是否使用了 4dp 倍数的间距?
- [ ] 是否让组件自动处理默认样式(而非手动设置)?
- [ ] 如果必须自定义,是否在
theme_config.dart中定义?
实施优先级
P0(必须遵守):
- 永远不硬编码
Colors.xxx,必须使用colorScheme - 永远不硬编码
fontSize,必须使用textTheme或不设置
- 永远不硬编码
P1(强烈推荐):
- 使用 Material 3 标准组件而非手动构建
- 使用 4dp 倍数间距
- 让组件自动处理默认样式
P2(最佳实践):
- 在
theme_config.dart中集中定义自定义值 - 使用语义化命名(如
primaryAction而非blueButton)
- 在
Claude 学习建议
每次会话开始时,Claude 应:
- 查看 CLAUDE.md 中的 Material 3 规范引用
- 检查项目
theme_config.dart中定义的颜色和文本样式 - 在实施前对照本文档的决策矩阵
反模式总结
所有需要避免的错误模式汇总
颜色反模式
| ❌ 错误 | ✅ 正确 | 原因 |
|---|---|---|
Colors.blue | colorScheme.primary | 不适配深色模式 |
Color(0xFF2196F3) | colorScheme.primary | 不语义化 |
Colors.grey[600] | colorScheme.onSurfaceVariant | 不语义化 |
Theme.of(context).primaryColor | colorScheme.primary | 已废弃 |
文本样式反模式
| ❌ 错误 | ✅ 正确 | 原因 |
|---|---|---|
fontSize: 15 | bodyLarge 或 bodyMedium | 非标准尺寸 |
fontSize: 14, fontWeight: w400 | 不设置(默认) | 重复默认值 |
textTheme.bodyMedium.copyWith(color: xxx) | TextStyle(color: xxx) | 不必要的 copyWith |
间距反模式
| ❌ 错误 | ✅ 正确 | 原因 |
|---|---|---|
EdgeInsets.all(15) | EdgeInsets.all(16) | 非4倍数 |
SizedBox(height: 10) | SizedBox(height: 8) 或 12 | 非4倍数 |
组件反模式
| ❌ 错误 | ✅ 正确 | 原因 |
|---|---|---|
| 手动构建按钮(Container + InkWell) | FilledButton | 重复造轮子 |
Icon(..., color: colorScheme.onSurfaceVariant) 在 ListTile 中 | Icon(...) | ListTile 自动设置 |
Switch(value: v, onChanged: null) | Switch(value: v, onChanged: (v) {}) | activeColor 需设置 |
维护者:Reading Vocab Helper Team 问题反馈:请在项目中提 Issue 最后更新:2026-01-18