← Frontend / Flutter

07_主题与样式

Flutter 主题系统和样式管理

Flutter 没有独立的 CSS 文件,样式通过 ThemeData 全局配置,通过 Widget 属性局部覆盖。


ThemeData 全局主题

MaterialApp(
  theme: ThemeData(
    useMaterial3: true,         // 启用 Material 3(2022 之后推荐)

    // 颜色方案:从种子色自动生成完整调色板(类似 Tailwind CSS 色板)
    colorScheme: ColorScheme.fromSeed(
      seedColor: const Color(0xFF6750A4),
      brightness: Brightness.light,
    ),

    // 全局字体
    fontFamily: 'Noto Sans SC',

    // 文字主题(类似 CSS typography 规范)
    textTheme: const TextTheme(
      displayLarge:   TextStyle(fontSize: 57, fontWeight: FontWeight.w400),
      headlineMedium: TextStyle(fontSize: 28, fontWeight: FontWeight.w600),
      titleLarge:     TextStyle(fontSize: 22, fontWeight: FontWeight.w600),
      bodyLarge:      TextStyle(fontSize: 16, height: 1.6),
      bodyMedium:     TextStyle(fontSize: 14, height: 1.5),
      labelSmall:     TextStyle(fontSize: 11, letterSpacing: 0.5),
    ),

    // 组件主题
    appBarTheme: const AppBarTheme(
      centerTitle: true,
      elevation: 0,
    ),
    cardTheme: CardTheme(
      elevation: 2,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
    ),
    inputDecorationTheme: InputDecorationTheme(
      border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
      filled: true,
    ),
    elevatedButtonTheme: ElevatedButtonThemeData(
      style: ElevatedButton.styleFrom(
        minimumSize: const Size(double.infinity, 48),
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
      ),
    ),
    chipTheme: ChipThemeData(
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
    ),
  ),

  // 深色主题
  darkTheme: ThemeData(
    useMaterial3: true,
    colorScheme: ColorScheme.fromSeed(
      seedColor: const Color(0xFF6750A4),
      brightness: Brightness.dark,
    ),
  ),

  // 主题模式
  themeMode: ThemeMode.system, // 跟随系统
)

在组件中使用主题

Widget build(BuildContext context) {
  // 获取当前主题(类似 CSS var(--primary-color))
  final theme = Theme.of(context);
  final colors = theme.colorScheme;
  final textTheme = theme.textTheme;

  return Container(
    color: colors.surface,              // 表面色(卡片/页面背景)
    child: Column(
      children: [
        Text(
          '标题',
          style: textTheme.headlineMedium?.copyWith(
            color: colors.primary,      // 主色
          ),
        ),
        Text(
          '副标题',
          style: textTheme.bodyMedium?.copyWith(
            color: colors.onSurface.withOpacity(0.6), // 次要文字色
          ),
        ),
      ],
    ),
  );
}

colorScheme 常用色值:

属性 用途
primary 主色(按钮、链接)
onPrimary 主色上的文字色
secondary 辅助色
surface 卡片/表面背景色
onSurface 表面上的文字色
error 错误提示色
background 页面背景色

深色模式动态切换

// 用 Provider 管理主题模式
class ThemeProvider extends ChangeNotifier {
  ThemeMode _themeMode = ThemeMode.system;
  ThemeMode get themeMode => _themeMode;

  void setTheme(ThemeMode mode) {
    _themeMode = mode;
    notifyListeners();
  }

  void toggle() {
    _themeMode = _themeMode == ThemeMode.dark
        ? ThemeMode.light
        : ThemeMode.dark;
    notifyListeners();
  }
}

// MaterialApp 中接入
MaterialApp(
  themeMode: context.watch<ThemeProvider>().themeMode,
  ...
)

// 切换按钮
Switch(
  value: themeProvider.themeMode == ThemeMode.dark,
  onChanged: (_) => themeProvider.toggle(),
)

局部样式覆盖

// 局部覆盖主题(类似 CSS scoped)
Theme(
  data: Theme.of(context).copyWith(
    colorScheme: Theme.of(context).colorScheme.copyWith(
      primary: Colors.orange,  // 这个子树内的主色变为橙色
    ),
  ),
  child: ElevatedButton(
    onPressed: () {},
    child: const Text('橙色按钮'),
  ),
)

颜色工具

// 颜色定义方式
Colors.blue                        // Material 颜色
Colors.blue.shade300               // 色阶(100-900)
const Color(0xFF6750A4)            // HEX(ARGB 格式)
Color.fromRGBO(103, 80, 164, 1.0)  // RGB + 透明度

// 颜色操作
Colors.black.withOpacity(0.5)      // 带透明度(类似 rgba())
Colors.blue.withAlpha(128)         // Alpha 值(0-255)
Color.lerp(Colors.red, Colors.blue, 0.5)  // 颜色插值(用于动画)

TextStyle 速查

TextStyle(
  fontSize: 16,
  fontWeight: FontWeight.bold,       // w100 ~ w900 / bold / normal
  fontStyle: FontStyle.italic,       // 斜体
  color: Colors.black87,
  letterSpacing: 0.5,                // 字间距
  wordSpacing: 2.0,                  // 词间距
  height: 1.5,                       // 行高(字号的倍数)
  decoration: TextDecoration.underline,     // 下划线
  decorationColor: Colors.blue,
  decorationStyle: TextDecorationStyle.dashed,
  shadows: [
    Shadow(color: Colors.black38, blurRadius: 4, offset: Offset(2, 2)),
  ],
)

// 继承主题样式并覆盖部分属性(类似 CSS 继承)
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
  fontWeight: FontWeight.bold,
  color: Colors.blue,
)