Material 3 带来的最大改进之一是让几乎所有 widget 都从 ColorScheme 派生颜色。
但缺点也是很明显,哪个 widget 使用哪个颜色经常让我蒙逼,我经常搞不清楚某个widget的颜色配置。
比如说,你改变一个单独的颜色 primary,可能会影响按钮、开关、滑块、进度指示器、文本选择、复选框和许多其他组件。有些 widget 根据其状态(启用、禁用、选中、悬停、聚焦、按下)使用不同的颜色。
这篇文章是 Flutter Material widget 在没有任何自定义 ThemeData 覆盖的情况下,如何使用 ColorScheme 的完整参考。
ColorScheme 概览
Flutter 目前定义了以下颜色:
| 颜色标记 (Color Token) | 用途说明 (Purpose) | 用途说明 |
|---|---|---|
primary |
Primary accent color | 主强调色 / 核心品牌色 |
onPrimary |
Content shown on primary | 在主色上显示的文本或图标颜色 |
primaryContainer |
Filled containers | 主色容器颜色(用于填充容器) |
onPrimaryContainer |
Content on primaryContainer | 在主色容器上显示的文本或图标颜色 |
secondary |
Secondary accent | 次要强调色 |
onSecondary |
Content on secondary | 在次要色上显示的文本或图标颜色 |
secondaryContainer |
Secondary containers | 次要色容器颜色 |
onSecondaryContainer |
Content on secondaryContainer | 在次要色容器上显示的文本或图标颜色 |
tertiary |
Third accent | 第三强调色 |
onTertiary |
Content on tertiary | 在第三色上显示的文本或图标颜色 |
tertiaryContainer |
Tertiary containers | 第三色容器颜色 |
onTertiaryContainer |
Content on tertiaryContainer | 在第三色容器上显示的文本或图标颜色 |
error |
Error color | 错误状态颜色 |
onError |
Content on error | 在错误色上显示的文本或图标颜色 |
errorContainer |
Error container | 错误状态容器颜色 |
onErrorContainer |
Content on errorContainer | 在错误状态容器上显示的文本或图标颜色 |
surface |
Default surfaces | 默认常规表面颜色 |
onSurface |
Text/icons on surface | 在常规表面上显示的文本或图标颜色 |
surfaceContainerLowest |
Lowest elevation | 最低层级的表面容器颜色 |
surfaceContainerLow |
Low elevation | 较低层级的表面容器颜色 |
surfaceContainer |
Medium elevation | 中等层级的表面容器颜色 |
surfaceContainerHigh |
High elevation | 较高层级的表面容器颜色 |
surfaceContainerHighest |
Highest elevation | 最高层级的表面容器颜色 |
onSurfaceVariant |
Secondary text/icons | 次要表面上的文本或图标颜色 / 次要文本色 |
outline |
Borders | 边框/轮廓线颜色 |
outlineVariant |
Subtle borders | 微弱/次要边框线颜色 |
shadow |
Shadows | 阴影颜色 |
scrim |
Modal scrim | 模态背景遮罩(半透明层)颜色 |
inverseSurface |
Inverse surfaces | 反转表面颜色(用于与背景相反的暗/亮色组件,如 Snackbar) |
onInverseSurface |
Content on inverse surface | 在反转表面上显示的文本或图标颜色 |
inversePrimary |
Accent on inverse surfaces | 反转表面上的强调色 |
surfaceTint |
Elevation tint | 随层级(高度)变化的表面色调成分 |
各个 widget 的默认颜色
按钮
常规按钮使用以下颜色:
python
# Create a simple translated markdown table as requested by the user ("直接翻译就行").
md_translation = """# Material Design 3 组件颜色映射表
| 组件 (Widget) | 背景 (Background) | 前景 (Foreground) |
| :--- | :--- | :--- |
| `FilledButton` (实心按钮) | `primary` (主色) | `onPrimary` (主色上内容色) |
| `FilledButton.tonal` (色调实心按钮) | `secondaryContainer` (次要颜色容器) | `onSecondaryContainer` (次要容器上内容色) |
| `ElevatedButton` (浮雕按钮) | `surfaceContainerLow` (较低层级表面容器) | `primary` (主色) |
| `OutlinedButton` (线框按钮) | `transparent` (透明) | `primary` (主色) |
| `TextButton` (文本按钮) | `transparent` (透明) | `primary` (主色) |
| `IconButton` (图标按钮) | `transparent` (透明) | `onSurfaceVariant` (次要表面内容色) |
| `IconButton.filled` (实心图标按钮) | `primary` (主色) | `onPrimary` (主色上内容色) |
| `IconButton.filledTonal` (色调实心图标按钮) | `secondaryContainer` (次要颜色容器) | `onSecondaryContainer` (次要容器上内容色) |
| `IconButton.outlined` (线框图标按钮) | `transparent` (透明) | `onSurfaceVariant` (次要表面内容色) |
"""
with open("material_design3_buttons_translated.md", "w", encoding="utf-8") as f:
f.write(md_translation)
print("Done")
text
Done
Material Design 3 组件颜色映射表
| 组件 (Widget) | 背景 (Background) | 前景 (Foreground) |
|---|---|---|
FilledButton (实心按钮) |
primary (主色) |
onPrimary (主色上内容色) |
FilledButton.tonal (色调实心按钮) |
secondaryContainer (次要颜色容器) |
onSecondaryContainer (次要容器上内容色) |
ElevatedButton (浮雕按钮) |
surfaceContainerLow (较低层级表面容器) |
primary (主色) |
OutlinedButton (线框按钮) |
transparent (透明) |
primary (主色) |
TextButton (文本按钮) |
transparent (透明) |
primary (主色) |
IconButton (图标按钮) |
transparent (透明) |
onSurfaceVariant (次要表面内容色) |
IconButton.filled (实心图标按钮) |
primary (主色) |
onPrimary (主色上内容色) |
IconButton.filledTonal (色调实心图标按钮) |
secondaryContainer (次要颜色容器) |
onSecondaryContainer (次要容器上内容色) |
IconButton.outlined (线框图标按钮) |
transparent (透明) |
onSurfaceVariant (次要表面内容色) |
浮动按钮(Floating button)
| 组件 (Widget) | 背景 (Background) | 前景 (Foreground) |
|---|---|---|
FloatingActionButton (悬浮操作按钮) |
primaryContainer (主色容器) |
onPrimaryContainer (主色容器上内容色) |
FAB.small (小号悬浮操作按钮) |
primaryContainer (主色容器) |
onPrimaryContainer (主色容器上内容色) |
FAB.large (大号悬浮操作按钮) |
primaryContainer (主色容器) |
onPrimaryContainer (主色容器上内容色) |
FAB.extended (扩展悬浮操作按钮) |
primaryContainer (主色容器) |
onPrimaryContainer (主色容器上内容色) |
选择控件
| 组件 (Widget) | 选中状态 (Selected) | 未选中状态 (Unselected) |
|---|---|---|
Checkbox (复选框) |
primary (主色) |
outline (边框色) |
Radio (单选框) |
primary (主色) |
onSurfaceVariant (次要表面内容色) |
Switch thumb (开关滑块) |
onPrimary (主色上内容色) |
outline (边框色) |
Switch track (开关轨道) |
primary (主色) |
surfaceContainerHighest (最高层级表面容器) |
Slider active (滑块激活部分) |
primary (主色) |
- |
Slider inactive (滑块未激活部分) |
surfaceContainerHighest (最高层级表面容器) |
- |
RangeSlider active (范围滑块激活部分) |
primary (主色) |
- |
RangeSlider inactive (范围滑块未激活部分) |
surfaceContainerHighest (最高层级表面容器) |
- |
进度指示器
| 组件 (Widget) | 颜色标记 (Color) | 中文翻译与说明 |
|---|---|---|
CircularProgressIndicator |
primary |
圆形进度条(使用主色) |
LinearProgressIndicator |
primary |
条形/线性进度条(使用主色) |
轨道颜色默认使用 surfaceContainer 颜色。
Chips
默认值根据 chip 类型和状态略有不同。下表显示了没有 ChipThemeData 覆盖时使用的颜色。
| 组件 (Widget) | 未选中背景 (Background unselected) | 未选中前景 (Foreground unselected) | 选中背景 (Background selected) | 选中前景 (Foreground selected) |
|---|---|---|---|---|
AssistChip (辅助标签) |
surface (表面色) |
onSurface (表面内容色) |
--- (不可选中) (not selectable) | --- |
SuggestionChip (建议标签) |
surface (表面色) |
onSurface (表面内容色) |
--- (不可选中) (not selectable) | --- |
FilterChip (过滤标签) |
surface (表面色) |
onSurfaceVariant (次要表面内容色) |
secondaryContainer (次要颜色容器) |
onSecondaryContainer (次要容器上内容色) |
ChoiceChip (选择标签) |
surface (表面色) |
onSurfaceVariant (次要表面内容色) |
secondaryContainer (次要颜色容器) |
onSecondaryContainer (次要容器上内容色) |
InputChip (输入标签) |
surface (表面色) |
onSurface (表面内容色) |
secondaryContainer (次要颜色容器) |
onSecondaryContainer (次要容器上内容色) |
Chip 轮廓在显示轮廓时使用 outline(例如某些 FilterChip 和 InputChip 变体)。
导航
| 组件 (Widget) | 背景 (Background) | 选中指示器 (Selected indicator) | 选中图标/文本 (Selected icon/text) | 未选中图标/文本 (Unselected icon/text) |
|---|---|---|---|---|
NavigationBar (底部导航栏) |
surface (表面色) |
secondaryContainer (次要颜色容器) |
onSecondaryContainer (次要容器上内容色) |
onSurfaceVariant (次要表面内容色) |
NavigationRail (导航侧栏/侧边栏) |
surface (表面色) |
secondaryContainer (次要颜色容器) |
onSecondaryContainer (次要容器上内容色) |
onSurfaceVariant (次要表面内容色) |
NavigationDrawer (导航抽屉) |
surface (表面色) |
secondaryContainer (次要颜色容器) |
onSecondaryContainer (次要容器上内容色) |
onSurfaceVariant (次要表面内容色) |
BottomNavigationBar (legacy) (传统底部导航栏) |
surface (表面色) |
--- | primary (主色) |
onSurfaceVariant (次要表面内容色) |
TabBar (标签栏) |
surface * (表面色) |
指示器: primary (Indicator: 主色) |
primary (主色) |
onSurfaceVariant (次要表面内容色) |
TabBar本身不绘制背景。它继承其父元素的背景,通常是AppBar(surface)或其他Material。
备注:
NavigationBar、NavigationRail和NavigationDrawer都遵循相同的 Material 3 颜色模型:- 容器:
surface - 选中指示器:
secondaryContainer - 选中标签/图标:
onSecondaryContainer - 未选中标签/图标:
onSurfaceVariant BottomNavigationBar是 Material 2 组件。它不使用选择指示器;选中项简单地使用primary颜色。TabBar使用primary作为指示器和标签/图标的高亮色,而未选中标签使用onSurfaceVariant。- 禁用目标使用降低不透明度的前景色版本,而不是不同的
ColorScheme条目。
应用栏(App bars)
| 组件 (Widget) | 背景 (Background) | 标题 (Title) | 图标 (Icons) | 操作按钮 (Actions) |
|---|---|---|---|---|
AppBar (应用栏/常驻顶栏) |
surface (表面色) |
onSurface (表面内容色) |
onSurface (表面内容色) |
onSurface (表面内容色) |
SliverAppBar (可滚动折叠顶栏) |
surface (表面色) |
onSurface (表面内容色) |
onSurface (表面内容色) |
onSurface (表面内容色) |
BottomAppBar (底部应用栏) |
surfaceContainer (表面容器色) |
onSurface * (表面内容色) |
onSurface (表面内容色) |
onSurface (表面内容色) |
BottomAppBar通常用作 widget(如IconButton)的容器,而不是显示标题。如果直接在里面放置文本,默认前景色是onSurface。
备注:
- 在 Material 3 中,
AppBar不再默认使用primary配onPrimary;它使用中性的surface颜色配onSurface内容。 - 前导图标、操作图标和标题都默认使用
onSurface。 - 滚动到底部时状态会改变背景为提升的 surface(使用 Material elevation 系统),但前景色保持
onSurface。 BottomAppBar使用surfaceContainer来与主页面的 surface 略有区分,同时保持相同的onSurface前景色。
卡片
| 组件 (Widget) | 背景 (Background) | 默认文本 (Default text) | 次要文本 (Secondary text) | 图标 (Icons) | 边框 (Border) |
|---|---|---|---|---|---|
Card (常规卡片/高亮卡片) |
surfaceContainerLow (较低层级表面容器) |
onSurface (表面内容色) |
onSurfaceVariant (次要表面内容色) |
onSurfaceVariant (次要表面内容色) |
--- |
FilledCard (填充卡片) |
surfaceContainerHighest (最高层级表面容器) |
onSurface (表面内容色) |
onSurfaceVariant (次要表面内容色) |
onSurfaceVariant (次要表面内容色) |
--- |
OutlinedCard (线框/描边卡片) |
surface (表面色) |
onSurface (表面内容色) |
onSurfaceVariant (次要表面内容色) |
onSurfaceVariant (次要表面内容色) |
outlineVariant (次要边框线颜色) |
备注:
Card、FilledCard和OutlinedCard只定义容器颜色(对于OutlinedCard,还有边框颜色)。- 文本和图标从周围主题继承颜色。标准
Textwidget 使用onSurface,而次要文本(例如TextTheme.bodySmall)和许多 Material 图标使用onSurfaceVariant。 - 卡片本身不覆盖前景色,不像
FilledButton或AlertDialog等组件。这使它们适合作为可以包含任意内容的中性 surface。
边框:outlineVariant
对话框
| 组件 (Widget) | 背景 (Background) | 标题 (Title) | 内容 (Content) | 图标 (Icons) | 操作按钮 (Actions) |
|---|---|---|---|---|---|
AlertDialog (警告对话框) |
surfaceContainerHigh (较高层级表面容器) |
onSurface (表面内容色) |
onSurfaceVariant (次要表面内容色) |
secondary (次要颜色) |
primary (主色) |
Dialog (基础对话框) |
surfaceContainerHigh (较高层级表面容器) |
onSurface * (表面内容色) |
onSurface * (表面内容色) |
onSurface * (表面内容色) |
primary (主色) |
SimpleDialog (简单对话框) |
surfaceContainerHigh (较高层级表面容器) |
onSurface (表面内容色) |
onSurfaceVariant (次要表面内容色) |
secondary (次要颜色) |
--- |
DatePickerDialog (日期选择对话框) |
surfaceContainerHigh (较高层级表面容器) |
onSurface (表面内容色) |
onSurface (表面内容色) |
primary (主色) |
primary (主色) |
TimePickerDialog (时间选择对话框) |
surfaceContainerHigh (较高层级表面容器) |
onSurface (表面内容色) |
onSurface (表面内容色) |
primary (主色) |
primary (主色) |
Dialog只是一个容器。它本身不定义标题、内容或图标颜色------这些取决于你放在里面的 widget。如果你使用标准的Text、Icon和TextButtonwidget,它们分别解析为onSurface、onSurface和primary。
备注:
AlertDialog和SimpleDialog有意使用onSurfaceVariant作为正文文本,使其比标题强调度更低。- 对话框图标(
AlertDialog.icon和SimpleDialog)默认使用secondary,符合 Material 3 规范。 - 对话框操作按钮通常是
TextButton,所以它们的前景色是primary。 - 所有对话框在 Material 3 中默认使用
surfaceContainerHigh作为容器颜色。
菜单
| 组件 (Widget) | 背景 (Background) | 文本 (Text) | 图标 (Icons) | 选中/聚焦项 (Selected/Focused item) |
|---|---|---|---|---|
MenuBar (菜单栏) |
surfaceContainer (中等层级表面容器) |
onSurface (表面内容色) |
onSurface (表面内容色) |
背景: secondaryContainer 前景: onSecondaryContainer |
MenuAnchor (菜单锚点/菜单面板) |
surfaceContainer (中等层级表面容器) |
onSurface (表面内容色) |
onSurface (表面内容色) |
背景: secondaryContainer 前景: onSecondaryContainer |
PopupMenuButton (弹出菜单按钮) |
surfaceContainer (中等层级表面容器) |
onSurface (表面内容色) |
onSurfaceVariant (次要表面内容色) |
背景: secondaryContainer 前景: onSecondaryContainer |
DropdownMenu (下拉菜单) |
菜单背景: surfaceContainer 文本框(填充时): surfaceContainerHighest |
onSurface (表面内容色) |
onSurfaceVariant (次要表面内容色) |
背景: secondaryContainer 前景: onSecondaryContainer |
备注:
- 菜单 surface 本身使用
surfaceContainer。 - 菜单项标签默认使用
onSurface。 - 前导和尾随图标通常使用
onSurfaceVariant(MenuBar除外,它们继承onSurface)。 - 高亮(悬停、聚焦或键盘选中)的菜单项使用
secondaryContainer配onSecondaryContainer,与 Material 3 其他选择模式一致。 - 禁用的菜单项使用降低不透明度的正常前景色版本,而不是不同的
ColorScheme颜色。
这是 Material 3 非常一致的一个领域:几乎每个菜单组件都使用 surfaceContainer 作为弹出 surface,用 secondaryContainer 来表示当前高亮项。
列表
Material Design 3 列表项组件 (ListTile Widget) 颜色映射表
| 组件 (Widget) | 背景 (Background) | 标题 (Title) | 副标题 (Subtitle) | 前置/后置组件 (Leading/Trailing) | 选中状态 (Selected) |
|---|---|---|---|---|---|
ListTile (列表项) |
surface (表面色) |
onSurface (表面内容色) |
onSurfaceVariant (次要表面内容色) |
onSurfaceVariant (次要表面内容色) |
标题与前置/后置组件变为主色: Title & leading/trailing → primary |
补充说明:
- 即使
selected为true,背景仍然是surface,除非你显式设置selectedTileColor。 - 当
selected: true时,标题和前导/尾随图标变为primary。 - 副标题不会 变为
primary;它保持onSurfaceVariant。 - 禁用的列表项降低这些颜色的不透明度,而不是切换到不同的
ColorScheme条目。 - 如果提供了
textColor或iconColor,它们会覆盖这些默认值。
这是少数几个不同文本元素有意默认使用不同语义颜色的 Material widget 之一。
输入字段
TextField 和 TextFormField
| 输入框构成元素 (Element) | 默认状态 (Default) | 聚焦状态 (Focused) | 错误状态 (Error) |
|---|---|---|---|
| 填充型背景 (Background - filled) | surfaceContainerHighest (最高层级表面容器) |
surfaceContainerHighest (最高层级表面容器) |
surfaceContainerHighest (最高层级表面容器) |
| 线框型背景 (Background - outlined) | transparent (透明) |
transparent (透明) |
transparent (透明) |
| 输入文本 (Input text) | onSurface (表面内容色) |
onSurface (表面内容色) |
onSurface (表面内容色) |
| 光标 (Cursor) | primary (主色) |
primary (主色) |
error (错误提示色) |
| 标签文本 (Label) | onSurfaceVariant (次要表面内容色) |
primary (主色) |
error (错误提示色) |
| 提示文本 (Hint) | onSurfaceVariant (次要表面内容色) |
onSurfaceVariant (次要表面内容色) |
onSurfaceVariant (次要表面内容色) |
| 辅助文本 (Helper text) | onSurfaceVariant (次要表面内容色) |
onSurfaceVariant (次要表面内容色) |
--- |
| 错误文本 (Error text) | --- | --- | error (错误提示色) |
| 边框线 (Border) | outline (边框/轮廓线颜色) |
primary (主色) |
error (错误提示色) |
| 前缀/后缀图标 (Prefix/Suffix icons) | onSurfaceVariant (次要表面内容色) |
onSurfaceVariant (次要表面内容色) |
error (错误提示色) |
备注:
- 表格假设使用 Material 3 提供的默认
InputDecorationTheme。 - 填充和轮廓字段只在容器颜色上不同;大多数前景色是相同的。
- 输入文本在所有状态下保持
onSurface。 - 当字段进入错误状态时,光标、标签、边框和前缀/后缀图标都变为
error。 - 占位符(
hintText)和辅助文本无论是否聚焦都继续使用onSurfaceVariant。
文本选择
| 元素 (Element) | 颜色权标 (Color) |
|---|---|
Cursor (光标) |
primary (主色) |
Selection (选中的文本区域) |
primary (with reduced opacity) (主色,降低不透明度/半透明) |
Selection handles (文本选择控制柄/水滴光标) |
primary (主色) |
SnackBar
| 属性 (Property) | 颜色权标 (Color) |
|---|---|
Background (背景) |
inverseSurface (反转表面色) |
Text (文本) |
onInverseSurface (反转表面内容色) |
Action (操作按钮) |
inversePrimary (反转表面上的强调色) |
Tooltips
| 属性 (Property) | 颜色权标 (Color) |
|---|---|
Background (背景) |
inverseSurface (反转表面色) |
Text (文本) |
onInverseSurface (反转表面内容色) |
Badges
| 属性 (Property) | 颜色权标 (Color) |
|---|---|
Background (背景) |
error (错误状态色) |
Text (文本) |
onError (在错误色上显示的文本颜色) |
Material 3 有意对 badge 使用 error/onError,但这是否合适取决于 badge 的含义。 |
分隔线
| 组件 (Widget) | 颜色权标 (Color) |
|---|---|
Divider (水平分隔线) |
outlineVariant (微弱/次要边框线颜色) |
VerticalDivider (垂直分隔线) |
outlineVariant (微弱/次要边框线颜色) |
滚动条
| 组件 (Widget) | 颜色权标 (Color) |
|---|---|
Scrollbar thumb (滚动条滑块) |
onSurfaceVariant (次要表面内容色) |
Material
一个普通的 Material widget 背景默认为 surface。
根据 elevation,它可能使用以下之一:
- surfaceContainerLowest
- surfaceContainerLow
- surfaceContainer
- surfaceContainerHigh
- surfaceContainerHighest
完整的 ColorScheme 使用
这一节回答反向问题:
"如果我改变一个颜色,哪些 widget 会改变?"
| 颜色权标 (ColorScheme color) | 被哪些组件/元素使用 (Used by) |
|---|---|
primary |
FilledButton、TextButton、OutlinedButton、Checkbox、Radio、Slider、ProgressIndicator、TextField 聚焦状态、TabBar、BottomNavigationBar、光标 (cursor)、选择区域 (selection)、Switch 轨道 |
onPrimary |
FilledButton 文本、IconButton.filled、Switch 滑块键 |
primaryContainer |
FloatingActionButton (悬浮操作按钮) |
onPrimaryContainer |
FloatingActionButton 的图标/文本 |
secondaryContainer |
FilledButton.tonal、NavigationBar 指示器、NavigationRail 指示器、NavigationDrawer 指示器、选中的 Chips (标签页) |
onSecondaryContainer |
音调填充按钮 (Tonal button) 文本、选中的 chip 文本 |
error |
错误提示文本、错误状态边框、徽章 (Badge) |
onError |
徽章文本 (Badge text) |
surface |
AppBar、Material 组件、ListTile |
surfaceContainerLow |
ElevatedButton (浮雕按钮)、Card (卡片) |
surfaceContainer |
BottomAppBar (底部应用栏)、menus (各种菜单) |
surfaceContainerHigh |
Dialogs (各种对话框) |
surfaceContainerHighest |
填充型 TextField (输入框) 背景、未激活的 slider (滑块) 轨道 |
onSurface |
默认文本/图标 |
onSurfaceVariant |
次要文本、提示文本 (hints)、图标、Radio、Scrollbar (滚动条) |
outline |
线框型 TextField、Checkbox 边框 |
outlineVariant |
Divider (分隔线)、Card 边框 |
最后的想法
Flutter 的 Material 3 实现非常一致。大多数 widget 不再定义自己的颜色,而是从 ColorScheme 派生,允许通过仅更改少量值来重新主题整个应用。
然而映射并不总是显而易见的。将这样的参考放在手边,在追踪为什么改变一个颜色会影响看似不相关的 widget 时,可以节省大量时间。