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)

相关推荐
kTR2hD1qb1 分钟前
Flutter 复杂拖拽排序实战:同源排序 + 跨容器拖拽完整落地
flutter
qq3621967052 分钟前
facebook是什么意思?新手从零到精通完全指南
运维·服务器·facebook
GISer_Jing6 分钟前
前端沙箱开源项目推荐(React/Next/Vue优先)
前端·react.js·开源
勿芮介10 分钟前
【研发工具】Jenkins镜像源配置问题及解决方案
运维·servlet·jenkins
云水一下10 分钟前
CSS3从零基础到精通(三):动感地带——过渡、动画、变形与响应式
前端·css3
qq_3129201110 分钟前
如何将Nginx响应时间从500ms降至50ms
运维·nginx
zizle_lin39 分钟前
CentOS配置yum源
linux·运维·centos
KaMeidebaby42 分钟前
卡梅德生物技术快报|Western Blot 实验应用:肺肠轴机制研究全流程技术解析
前端·数据库·人工智能·算法·百度
志栋智能1 小时前
超自动化运维:如何降低人为错误?
大数据·运维·网络·人工智能·自动化
达达爱吃肉1 小时前
claude 接入deepseek 运行报错
java·服务器·前端