3、自定义主题颜色:深入 ColorScheme,创建品牌专属调色板,覆盖默认颜色
说实话,Flutter 默认的 Material Design 颜色挺好看的。但你要是做商业项目,客户第一句话往往是:「能不能换成我们公司的品牌色?」
嗯,这时候就得跟 ColorScheme 打交道了。我刚开始接触时也觉得这东西有点绕,但摸清楚规律后,你会发现它其实就是一个「颜色管理系统」——你给它一套品牌色,它帮你搞定全局的明暗适配、状态变化。
3.1 ColorScheme 到底是什么?
说白了,ColorScheme 就是 Flutter 里定义的一套「语义化颜色」。它不像以前那样让你手动去配每个按钮、每个背景的颜色,而是定义好「主色」、「次要色」、「错误色」等角色,框架自动帮你应用到各个组件上。
举个例子:
// 默认的 ColorScheme
ColorScheme.defaultScheme
.copyWith(primary: Colors.blue)
这样一改,所有用到 primary 的地方——FloatingActionButton、AppBar、选中状态——全都会变成蓝色。省心吧?
ColorScheme.fromSeed 或 ColorScheme.fromSwatch 生成一套完整的调色板。这样明暗色系会自动匹配,省去手动配对的麻烦。
3.2 从品牌色生成调色板
假设你们公司的品牌色是 #FF6B35(一种活力橙)。你当然可以手动配一套深浅色,但更聪明的做法是让 Flutter 帮你算。
final brandColor = const Color(0xFFFF6B35);
final lightScheme = ColorScheme.fromSeed(
seedColor: brandColor,
brightness: Brightness.light,
);
final darkScheme = ColorScheme.fromSeed(
seedColor: brandColor,
brightness: Brightness.dark,
);
你看,只需要一个种子颜色,Flutter 就会自动生成 primary、secondary、surface、onPrimary 等十几个颜色。而且明暗两套都给你算好了。
我在项目中遇到过一个问题:客户给的品牌色只有一种,但我们需要支持深色模式。手动配深色版太痛苦了,用 fromSeed 几行代码就搞定。客户看了深色模式的效果,直呼「你们怎么知道我想要的暗橙色是这样的?」
3.3 手动覆盖特定颜色
自动生成虽然方便,但有时候品牌规范里明确规定了某些颜色。比如品牌手册里写着「主色 #FF6B35,次要色 #2D3142,背景色 #F8F9FA」。
这时候就需要手动覆盖了:
final customScheme = ColorScheme(
brightness: Brightness.light,
primary: const Color(0xFFFF6B35),
onPrimary: Colors.white,
secondary: const Color(0xFF2D3142),
onSecondary: Colors.white,
surface: const Color(0xFFF8F9FA),
onSurface: const Color(0xFF1A1A2E),
error: const Color(0xFFD32F2F),
onError: Colors.white,
);
onPrimary、onSurface 这些「反色」。结果按钮上的文字变成了白色底+白色字,完全看不见。记住:每个颜色都要配一个对应的「前景色」,否则 UI 会出大问题。
3.4 覆盖默认颜色的最佳实践
你可能会问:是不是所有颜色都要手动配?当然不是。我建议采用「分层覆盖」的策略:
| 层级 | 方法 | 适用场景 |
|---|---|---|
| 第一层 | ColorScheme.fromSeed |
只有品牌主色,其他让框架自动生成 |
| 第二层 | .copyWith |
自动生成后,微调个别颜色 |
| 第三层 | 手动创建 ColorScheme |
品牌规范严格,每个颜色都有明确值 |
我个人习惯先用 fromSeed 生成,再用 copyWith 覆盖不符合品牌规范的地方。这样既省事,又不会偏离品牌太远。
final baseScheme = ColorScheme.fromSeed(
seedColor: brandColor,
brightness: Brightness.light,
);
final finalScheme = baseScheme.copyWith(
secondary: const Color(0xFF2D3142),
surface: const Color(0xFFF8F9FA),
);
3.5 应用到 ThemeData
调色板配好了,怎么用?很简单,塞进 ThemeData 里:
final theme = ThemeData(
useMaterial3: true,
colorScheme: finalScheme,
);
然后整个 App 的 Material 组件都会自动适配这套颜色。你想想看,以前改个主题色要改多少个地方?AppBar、Button、TextField、Switch……现在一个 colorScheme 全搞定。
useMaterial3: true。Material 2 虽然也支持 ColorScheme,但 Material 3 对颜色的语义化更彻底,组件对 colorScheme 的响应也更完整。
3.6 实战:品牌色 + 深色模式
最后给你一个完整的实战模板。我在好几个项目里都用这个结构:
class AppTheme {
static const Color _brandColor = Color(0xFFFF6B35);
static ThemeData light() {
final scheme = ColorScheme.fromSeed(
seedColor: _brandColor,
brightness: Brightness.light,
).copyWith(
secondary: const Color(0xFF2D3142),
);
return ThemeData(
useMaterial3: true,
colorScheme: scheme,
);
}
static ThemeData dark() {
final scheme = ColorScheme.fromSeed(
seedColor: _brandColor,
brightness: Brightness.dark,
).copyWith(
secondary: const Color(0xFFB0BEC5),
);
return ThemeData(
useMaterial3: true,
colorScheme: scheme,
);
}
}
使用时:
MaterialApp(
theme: AppTheme.light(),
darkTheme: AppTheme.dark(),
themeMode: ThemeMode.system,
)
嗯,这样一套品牌专属的调色板就搞定了。用户切深色模式时,颜色会自动切换,而且所有组件都保持一致风格。
light().copyWith(brightness: Brightness.dark),结果颜色对比度完全不对。记住:明暗两套调色板要分别用 fromSeed 生成,不要偷懒只改一个 brightness 属性。
总结一下:ColorScheme 是 Flutter 主题定制的核心。从品牌色出发,用 fromSeed 自动生成,再用 copyWith 微调,最后塞进 ThemeData。这套流程我用了两年,稳得很。