是什么
ThemeData
是 Flutter 提供的一个 全局主题配置类,用来定义应用的 颜色、字体、按钮样式、AppBar 风格、图标主题 等等。
它是 Material Design 风格 的核心配置入口。
- 通过
ThemeData
,你可以让整个应用的 视觉效果保持一致。 - 一般在
MaterialApp
的theme
、darkTheme
、themeMode
属性中设置。
常见属性
属性 | 类型 | 说明 |
---|---|---|
colorScheme | ColorScheme |
推荐方式,定义整个应用的颜色体系 |
useMaterial3 | bool |
是否启用 Material 3 样式(默认 true) |
brightness | Brightness |
明亮/暗黑模式 |
primaryColor | Color |
主色(已过时,推荐用 colorScheme.primary ) |
scaffoldBackgroundColor | Color |
Scaffold 背景色 |
appBarTheme | AppBarTheme |
AppBar 样式(背景、文字、图标等) |
iconTheme | IconThemeData |
图标默认样式 |
textTheme | TextTheme |
全局文字样式(M3 新命名) |
elevatedButtonTheme | ElevatedButtonThemeData |
ElevatedButton 样式 |
filledButtonTheme | FilledButtonThemeData |
FilledButton 样式(M3 推荐按钮) |
outlinedButtonTheme | OutlinedButtonThemeData |
OutlinedButton 样式 |
inputDecorationTheme | InputDecorationTheme |
输入框样式 |
cardTheme | CardTheme |
Card 组件样式 |
chipTheme | ChipThemeData |
Chip 样式 |
dividerTheme | DividerThemeData |
Divider 分割线样式 |
dialogTheme | DialogTheme |
对话框样式 |
snackBarTheme | SnackBarThemeData |
Snackbar 样式 |
bottomSheetTheme | BottomSheetThemeData |
BottomSheet 样式 |
navigationBarTheme | NavigationBarThemeData |
新版 NavigationBar 样式(取代 BottomNavigationBar) |
canvasColor | Color |
Material 类型组件的背景色 |
Brightness
Brightness
是一个枚举:
enum Brightness {
/// The color is dark and will require a light text color to achieve readable
/// contrast.
///
/// For example, the color might be dark grey, requiring white text.
dark,
/// The color is light and will require a dark text color to achieve readable
/// contrast.
///
/// For example, the color might be bright white, requiring black text.
light,
}
在 Flutter(以及 Material Design)里,Brightness
并不是颜色本身,而是一个 环境标签,告诉系统:当前主题(或配色方案)是偏「浅色」(Brightness.light
)还是偏「深色」(Brightness.dark
)。
- 如果
brightness == Brightness.light
→ 系统假设背景是亮色(一般是白色),所以文字默认用深色(黑/灰)。 - 如果
brightness == Brightness.dark
→ 系统假设背景是暗色(一般是黑色),所以文字默认用浅色(白/灰)。
也就是说,Brightness 不是具体的颜色,而是一个上下文信息,帮助 Flutter 自动选择合适的对比色。
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Brightness _brightness = Brightness.light;
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
// 关键点:这里只切换 brightness
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.blue,
brightness: _brightness,
),
),
home: Scaffold(
appBar: AppBar(title: const Text("Brightness Demo")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 自动跟随亮/暗模式变化
Text("Hello Brightness!", style: TextStyle(fontSize: 35)),
// 强制指定颜色(不随亮/暗模式变化)
Text(
"Always Black",
style: TextStyle(fontSize: 35, color: Colors.black),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
_brightness = _brightness == Brightness.light
? Brightness.dark
: Brightness.light;
});
},
child: const Icon(Icons.brightness_6),
),
),
);
}
}
canvasColor
ThemeData.canvasColor
表示 “Material 类型组件的背景色”。它是一个比较底层的颜色配置,用来指定 绘制在“画布 (Canvas)”上的默认背景色。- Scaffold:在早期 Flutter 版本(Material2),
Scaffold
的默认背景色就是Theme.of(context).canvasColor
。 - 在 Material 3 下,大部分常见组件已经不直接用
canvasColor
,而是用ColorScheme.surface
。但canvasColor
依然存在,更多是为了兼容性或自定义组件时作为兜底。
三种颜色的区别:
属性 主要作用 默认应用场景 Material 2 状态 Material 3 状态 优先级说明 canvasColor 画布的默认背景色(底层用色) Material
、CustomPaint
等没有指定背景色时常用,很多组件默认取它 使用减少,多转向 ColorScheme.surface
兜底色,很多地方被覆盖 scaffoldBackgroundColor Scaffold
的背景色整个页面背景 默认从 canvasColor
继承默认从 ColorScheme.background
取优先级高于 canvasColor
,仅作用于 ScaffoldcolorScheme.surface 表面背景色(Material 3 推荐) 卡片、对话框、菜单、底部抽屉等 不常用 核心推荐色 Material 3 下组件优先使用它 - Scaffold:在早期 Flutter 版本(Material2),
什么是 Material Design
Material Design 是 Google 的 UI 设计语言,Flutter 的大部分控件都基于它。
从 2014 年开始,Google 已经迭代了三个版本:
- Material 1 (2014) → 经典纸片阴影风格。
- Material 2 (2018) → 更加扁平、统一,Flutter 早期默认用的就是 M2。
- Material 3 (Material You, 2021) → 个性化 + 动态配色,强调用户定制体验。
不同的Material Design,其内置组件的风格会不一样。
import 'package:flutter/material.dart';
void main() {
runApp(const MaterialDemoApp());
}
class MaterialDemoApp extends StatefulWidget {
const MaterialDemoApp({super.key});
@override
State<MaterialDemoApp> createState() => _MaterialDemoAppState();
}
class _MaterialDemoAppState extends State<MaterialDemoApp> {
bool _useMaterial3 = false; // 默认用 Material 2
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Material Design 切换示例',
theme: ThemeData(
useMaterial3: _useMaterial3,
colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
),
home: Scaffold(
appBar: AppBar(
title: Text(_useMaterial3 ? 'Material 3 样式' : 'Material 2 样式'),
centerTitle: true,
actions: [
Switch(
value: _useMaterial3,
onChanged: (value) {
setState(() {
_useMaterial3 = value;
});
},
),
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {},
child: const Text("ElevatedButton"),
),
const SizedBox(height: 16),
FilledButton(
onPressed: () {},
child: const Text("FilledButton (M3 新增)"),
),
const SizedBox(height: 16),
Card(
child: Padding(
padding: const EdgeInsets.all(20),
child: Text(
_useMaterial3
? "这是 M3 Card,更圆润,颜色来自 ColorScheme"
: "这是 M2 Card,圆角更小,风格偏扁平",
),
),
),
],
),
),
),
);
}
}
在子组件中使用主题
使用全局主题(Theme.of(context)
)
子组件可以直接读取最近的 Theme
(通常是 MaterialApp
的 theme
):
class MyTextWidget extends StatelessWidget {
const MyTextWidget({super.key});
@override
Widget build(BuildContext context) {
return Text(
"Hello Theme",
style: Theme.of(context).textTheme.bodyLarge, // 使用全局定义的字体样式
);
}
}
好处:保持全局风格一致,不需要重复写样式。
在局部覆盖主题(Theme
+ copyWith
)
如果只想在某个子树中修改部分主题,而不影响全局:
class MyButtonWidget extends StatelessWidget {
const MyButtonWidget({super.key});
@override
Widget build(BuildContext context) {
return Theme(
data: Theme.of(context).copyWith(
colorScheme: Theme.of(context).colorScheme.copyWith(
primary: Colors.red, // 覆盖主色
),
),
child: ElevatedButton(
onPressed: () {},
child: const Text("Red Theme Button"),
),
);
}
}
好处:只影响当前 Theme
包裹的子组件,不会污染全局。