6.1 主题与暗色模式

Flutter 的主题系统(ThemeData)提供了统一的视觉风格管理,通过 Material 3 的颜色系统和深色模式支持,可以轻松构建专业的视觉体系。


一、ThemeData 动态切换

1.1 定义双主题

dart 复制代码
class AppTheme {
  // 亮色主题
  static ThemeData get lightTheme => ThemeData(
    useMaterial3: true,
    brightness: Brightness.light,
    colorScheme: ColorScheme.fromSeed(
      seedColor: const Color(0xFF6750A4), // 主色调
      brightness: Brightness.light,
    ),
    // 文字主题
    textTheme: _buildTextTheme(Brightness.light),
    // 组件主题
    appBarTheme: const AppBarTheme(
      centerTitle: true,
      elevation: 0,
    ),
    cardTheme: CardTheme(
      elevation: 2,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
    ),
    elevatedButtonTheme: ElevatedButtonThemeData(
      style: ElevatedButton.styleFrom(
        minimumSize: const Size.fromHeight(48),
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
      ),
    ),
    inputDecorationTheme: InputDecorationTheme(
      border: OutlineInputBorder(
        borderRadius: BorderRadius.circular(12),
      ),
      filled: true,
    ),
  );

  // 暗色主题
  static ThemeData get darkTheme => ThemeData(
    useMaterial3: true,
    brightness: Brightness.dark,
    colorScheme: ColorScheme.fromSeed(
      seedColor: const Color(0xFF6750A4),
      brightness: Brightness.dark,
    ),
    textTheme: _buildTextTheme(Brightness.dark),
    appBarTheme: const AppBarTheme(centerTitle: true, elevation: 0),
    cardTheme: CardTheme(
      elevation: 4,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
    ),
  );

  static TextTheme _buildTextTheme(Brightness brightness) {
    final color = brightness == Brightness.light
        ? const Color(0xFF1C1B1F)
        : const Color(0xFFE6E1E5);
    return TextTheme(
      headlineLarge: TextStyle(fontSize: 32, fontWeight: FontWeight.bold, color: color),
      headlineMedium: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: color),
      titleLarge: TextStyle(fontSize: 20, fontWeight: FontWeight.w600, color: color),
      bodyLarge: TextStyle(fontSize: 16, color: color),
      bodyMedium: TextStyle(fontSize: 14, color: color.withOpacity(0.8)),
    );
  }
}

1.2 主题切换控制器

dart 复制代码
class ThemeController extends ChangeNotifier {
  ThemeMode _themeMode;

  ThemeController({ThemeMode initial = ThemeMode.system})
      : _themeMode = initial;

  ThemeMode get themeMode => _themeMode;

  bool get isDark => _themeMode == ThemeMode.dark;

  void setThemeMode(ThemeMode mode) {
    _themeMode = mode;
    PreferencesService.saveThemeMode(mode.index);
    notifyListeners();
  }

  void toggleTheme() {
    setThemeMode(isDark ? ThemeMode.light : ThemeMode.dark);
  }

  // 跟随系统
  void followSystem() => setThemeMode(ThemeMode.system);
}

// 在 App 入口
ChangeNotifierProvider(
  create: (_) => ThemeController(
    initial: ThemeMode.values[PreferencesService.getThemeMode()],
  ),
  child: Consumer<ThemeController>(
    builder: (context, themeController, _) => MaterialApp(
      theme: AppTheme.lightTheme,
      darkTheme: AppTheme.darkTheme,
      themeMode: themeController.themeMode,
      home: const HomePage(),
    ),
  ),
)

二、自定义颜色与字体系统

2.1 Material 3 颜色系统

dart 复制代码
// 从种子色生成完整颜色系统
final colorScheme = ColorScheme.fromSeed(
  seedColor: const Color(0xFF6750A4),
  brightness: Brightness.light,
);

// 颜色角色对应表
// primary         → 主要操作按钮背景
// onPrimary       → primary 上的文字/图标
// primaryContainer → 次要容器背景
// secondary       → 次要操作
// surface         → 卡片、对话框背景
// onSurface       → surface 上的文字
// error           → 错误状态

// 在 Widget 中使用
Widget build(BuildContext context) {
  final colorScheme = Theme.of(context).colorScheme;
  return Container(
    color: colorScheme.primaryContainer,
    child: Text(
      'Hello',
      style: TextStyle(color: colorScheme.onPrimaryContainer),
    ),
  );
}

2.2 扩展主题颜色(自定义颜色)

dart 复制代码
// 定义扩展颜色
@immutable
class AppColors extends ThemeExtension<AppColors> {
  final Color success;
  final Color warning;
  final Color info;
  final Color gradientStart;
  final Color gradientEnd;

  const AppColors({
    required this.success,
    required this.warning,
    required this.info,
    required this.gradientStart,
    required this.gradientEnd,
  });

  @override
  AppColors copyWith({
    Color? success,
    Color? warning,
    Color? info,
    Color? gradientStart,
    Color? gradientEnd,
  }) {
    return AppColors(
      success: success ?? this.success,
      warning: warning ?? this.warning,
      info: info ?? this.info,
      gradientStart: gradientStart ?? this.gradientStart,
      gradientEnd: gradientEnd ?? this.gradientEnd,
    );
  }

  @override
  AppColors lerp(AppColors? other, double t) {
    if (other == null) return this;
    return AppColors(
      success: Color.lerp(success, other.success, t)!,
      warning: Color.lerp(warning, other.warning, t)!,
      info: Color.lerp(info, other.info, t)!,
      gradientStart: Color.lerp(gradientStart, other.gradientStart, t)!,
      gradientEnd: Color.lerp(gradientEnd, other.gradientEnd, t)!,
    );
  }

  // 亮色配置
  static const light = AppColors(
    success: Color(0xFF4CAF50),
    warning: Color(0xFFFF9800),
    info: Color(0xFF2196F3),
    gradientStart: Color(0xFF6750A4),
    gradientEnd: Color(0xFF03DAC6),
  );

  // 暗色配置
  static const dark = AppColors(
    success: Color(0xFF81C784),
    warning: Color(0xFFFFB74D),
    info: Color(0xFF64B5F6),
    gradientStart: Color(0xFFD0BCFF),
    gradientEnd: Color(0xFF6FF7EC),
  );
}

// 注册到 ThemeData
ThemeData get lightTheme => ThemeData(
  extensions: const [AppColors.light],
  // ...
)

// 在 Widget 中使用
final appColors = Theme.of(context).extension<AppColors>()!;
Container(color: appColors.success)

2.3 自定义字体

yaml 复制代码
# pubspec.yaml
flutter:
  fonts:
    - family: Outfit
      fonts:
        - asset: assets/fonts/Outfit-Regular.ttf
          weight: 400
        - asset: assets/fonts/Outfit-Medium.ttf
          weight: 500
        - asset: assets/fonts/Outfit-Bold.ttf
          weight: 700
dart 复制代码
ThemeData get theme => ThemeData(
  fontFamily: 'Outfit', // 全局字体
  textTheme: const TextTheme(
    headlineLarge: TextStyle(
      fontFamily: 'Outfit',
      fontSize: 32,
      fontWeight: FontWeight.w700,
    ),
  ),
)

2.4 动态主题色(用户自定义)

dart 复制代码
class DynamicThemeController extends ChangeNotifier {
  Color _seedColor = const Color(0xFF6750A4);

  Color get seedColor => _seedColor;

  ThemeData get lightTheme => ThemeData(
    useMaterial3: true,
    colorScheme: ColorScheme.fromSeed(
      seedColor: _seedColor,
      brightness: Brightness.light,
    ),
  );

  ThemeData get darkTheme => ThemeData(
    useMaterial3: true,
    colorScheme: ColorScheme.fromSeed(
      seedColor: _seedColor,
      brightness: Brightness.dark,
    ),
  );

  void updateSeedColor(Color color) {
    _seedColor = color;
    notifyListeners();
  }
}

小结

知识点 核心要点
ThemeData 定义全局视觉风格,lightTheme + darkTheme
ThemeMode system / light / dark 三种切换模式
ColorScheme.fromSeed 从种子色自动生成 Material 3 完整颜色系统
ThemeExtension 扩展自定义颜色,支持亮/暗色自动切换
字体配置 pubspec.yaml 注册,fontFamily 全局应用

👉 下一节:6.2 国际化(i18n)

相关推荐
小风吹啊吹~几秒前
vscode的tunnel链接(Linux 服务器 + Windows 本地电脑版本)
服务器·vscode·microsoft·远程工作
iCxhust几秒前
在 emu8086 中可以直接编译运行的完整汇编程序,演示数组的定义、遍历、求和、求最大值。
开发语言·前端·javascript·汇编·单片机·嵌入式硬件·算法
JianZhen✓5 分钟前
2026前端高频面试题总结(Vue/JS/网络/Webpack/性能优化/手写)
前端·javascript·vue.js
maaath7 分钟前
【无标题】Flutter for OpenHarmony 的文具手账应用开发实践
flutter·华为·harmonyos
Jinkxs9 分钟前
LoadBalancer- 常见负载均衡算法:轮询 / 加权轮询 / 最少连接等基础实现
运维·算法·负载均衡
里欧跑得慢9 分钟前
Flutter 主题管理:构建一致的用户界面
前端·css·flutter·web
eastyuxiao13 分钟前
流程图 + 配置清单 在团队 / 公司运维场景的落地应用方法
运维·人工智能·流程图
Yupureki15 分钟前
《Linux网络编程》4.应用层HTTP协议
linux·服务器·c语言·网络·c++·http
龙猫里的小梅啊22 分钟前
CSS(八)CSS显示模式display属性
前端·css·css3
雨季mo浅忆25 分钟前
第二项目重新梳理
前端·面试