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)

相关推荐
luyun0202026 分钟前
论坛里的小工具,吾爱出品
运维·服务器·windows
江湖有缘12 分钟前
Docker实战 | 使用Docker部署EasyNode服务器面板工具
服务器·docker·容器
2601_9657984717 分钟前
Arrow Unlocker HTML5 Game Review: Keep Web Arcade Players Coming Back
前端·html·html5
你怎么知道我是队长33 分钟前
计算机虚拟存储管理与页面置换算法详解
服务器·网络·算法
不简说1 小时前
JS 代码技巧 vol.10 — 20 个 V8 引擎原理,解释"为什么这样写快"
前端·javascript·面试
Miao121311 小时前
工程交付指标入门:如何重新掌控软件交付
大数据·运维·devops
涛声依旧393161 小时前
运维项目:InnoAI SQL 助手
运维·数据库·sql
醉城夜风~1 小时前
HTML常用标签详解学习博客:从基础标签到页面结构完整掌握
前端·学习·html
养海绵宝宝的小蜗1 小时前
LVS(Linux Virtual Server)项目知识点总结
linux·运维·lvs
OpenTiny社区1 小时前
拒绝臃肿!TinyRobot从“AI 输入框”到“AI输入控制台”的可扩展进化
前端·vue.js