flutter:全局主题配置ThemeData

  1. 是什么
  2. 常见属性
    1. Brightness
    2. canvasColor
  3. 什么是 Material Design
  4. 在子组件中使用主题
    1. 使用全局主题(Theme.of(context))
    2. 在局部覆盖主题(Theme + copyWith)

是什么

ThemeData 是 Flutter 提供的一个 全局主题配置类,用来定义应用的 颜色、字体、按钮样式、AppBar 风格、图标主题 等等。
它是 Material Design 风格 的核心配置入口。

  • 通过 ThemeData,你可以让整个应用的 视觉效果保持一致
  • 一般在 MaterialAppthemedarkThemethemeMode 属性中设置。

常见属性

属性 类型 说明
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 画布的默认背景色(底层用色) MaterialCustomPaint 等没有指定背景色时 常用,很多组件默认取它 使用减少,多转向 ColorScheme.surface 兜底色,很多地方被覆盖
    scaffoldBackgroundColor Scaffold 的背景色 整个页面背景 默认从 canvasColor 继承 默认从 ColorScheme.background 优先级高于 canvasColor,仅作用于 Scaffold
    colorScheme.surface 表面背景色(Material 3 推荐) 卡片、对话框、菜单、底部抽屉等 不常用 核心推荐色 Material 3 下组件优先使用它

什么是 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(通常是 MaterialApptheme):

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 包裹的子组件,不会污染全局。

×

喜欢就点赞,疼爱就打赏