Flutter样式与主题 #

一、ThemeData #

1.1 基本用法 #

ThemeData定义了应用的主题配置。

dart
ThemeData({
  bool? applyElevationOverlayColor,
  NoDefaultCupertinoThemeData? cupertinoOverrideTheme,
  Iterable<ThemeExtension>? extensions,
  InputDecorationTheme? inputDecorationTheme,
  MaterialTapTargetSize? materialTapTargetSize,
  PageTransitionsTheme? pageTransitionsTheme,
  TargetPlatform? platform,
  ScrollBehavior? scrollBehavior,
  TapDownDetails? tapDownDetails,
  ColorScheme? colorScheme,
  Brightness? brightness,
  Color? primaryColor,
  Color? primaryColorLight,
  Color? primaryColorDark,
  Color? canvasColor,
  Color? cardColor,
  Color? dividerColor,
  Color? highlightColor,
  Color? splashColor,
  InteractiveInkFeatureFactory? splashFactory,
  Color? hoverColor,
  Color? focusColor,
  Color? scaffoldBackgroundColor,
  Color? bottomAppBarColor,
  Color? dialogBackgroundColor,
  Color? indicatorColor,
  Color? hintColor,
  Color? errorColor,
  Color? toggleableActiveColor,
  TextTheme? textTheme,
  TextTheme? primaryTextTheme,
  IconThemeData? iconTheme,
  IconThemeData? primaryIconTheme,
  AppBarTheme? appBarTheme,
  ButtonThemeData? buttonTheme,
  CardTheme? cardTheme,
  ChipThemeData? chipTheme,
  ElevatedButtonThemeData? elevatedButtonTheme,
  FilledButtonThemeData? filledButtonTheme,
  OutlinedButtonThemeData? outlinedButtonTheme,
  TextButtonThemeData? textButtonTheme,
  FloatingActionButtonThemeData? floatingActionButtonTheme,
  IconButtonThemeData? iconButtonTheme,
  DialogTheme? dialogTheme,
  DividerThemeData? dividerTheme,
  ListTileThemeData? listTileTheme,
  NavigationBarThemeData? navigationBarTheme,
  NavigationRailThemeData? navigationRailTheme,
  TabBarTheme? tabBarTheme,
  DrawerThemeData? drawerTheme,
  SnackBarThemeData? snackBarTheme,
  BottomSheetThemeData? bottomSheetTheme,
  CheckboxThemeData? checkboxTheme,
  RadioThemeData? radioTheme,
  SwitchThemeData? switchTheme,
  ProgressIndicatorThemeData? progressIndicatorTheme,
  SliderThemeData? sliderTheme,
  TimePickerThemeData? timePickerTheme,
  DatePickerThemeData? datePickerTheme,
  DataTableThemeData? dataTableTheme,
  PopupMenuThemeData? popupMenuTheme,
  TooltipThemeData? tooltipTheme,
  BottomNavigationBarThemeData? bottomNavigationBarTheme,
  BottomAppBarTheme? bottomAppBarTheme,
  MenuBarThemeData? menuBarTheme,
  MenuStyle? menuButtonTheme,
  DropdownMenuThemeData? dropdownMenuTheme,
  SearchBarThemeData? searchBarTheme,
  SearchViewThemeData? searchViewTheme,
})

1.2 常用配置 #

dart
MaterialApp(
  theme: ThemeData(
    colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
    useMaterial3: true,
    brightness: Brightness.light,
    primaryColor: Colors.blue,
    scaffoldBackgroundColor: Colors.white,
    appBarTheme: AppBarTheme(
      centerTitle: true,
      elevation: 0,
      backgroundColor: Colors.blue,
      foregroundColor: Colors.white,
    ),
    elevatedButtonTheme: ElevatedButtonThemeData(
      style: ElevatedButton.styleFrom(
        backgroundColor: Colors.blue,
        foregroundColor: Colors.white,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(8),
        ),
      ),
    ),
    cardTheme: CardTheme(
      elevation: 4,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(12),
      ),
    ),
    inputDecorationTheme: InputDecorationTheme(
      border: OutlineInputBorder(
        borderRadius: BorderRadius.circular(8),
      ),
      filled: true,
      fillColor: Colors.grey.shade100,
    ),
  ),
)

二、ColorScheme #

2.1 从种子颜色生成 #

dart
ColorScheme.fromSeed({
  required Color seedColor,
  Brightness brightness = Brightness.light,
  Color? primary,
  Color? onPrimary,
  Color? primaryContainer,
  Color? onPrimaryContainer,
  Color? secondary,
  Color? onSecondary,
  Color? secondaryContainer,
  Color? onSecondaryContainer,
  Color? tertiary,
  Color? onTertiary,
  Color? tertiaryContainer,
  Color? onTertiaryContainer,
  Color? error,
  Color? onError,
  Color? errorContainer,
  Color? onErrorContainer,
  Color? surface,
  Color? onSurface,
  Color? surfaceContainerHighest,
  Color? onSurfaceVariant,
  Color? outline,
  Color? outlineVariant,
  Color? shadow,
  Color? scrim,
  Color? inverseSurface,
  Color? onInverseSurface,
  Color? inversePrimary,
  Color? surfaceTint,
})

ColorScheme.fromSeed(seedColor: Colors.blue)
ColorScheme.fromSeed(seedColor: Colors.blue, brightness: Brightness.dark)

2.2 颜色角色 #

dart
ThemeData(
  colorScheme: ColorScheme(
    brightness: Brightness.light,
    primary: Colors.blue,
    onPrimary: Colors.white,
    primaryContainer: Colors.blue.shade100,
    onPrimaryContainer: Colors.blue.shade900,
    secondary: Colors.teal,
    onSecondary: Colors.white,
    secondaryContainer: Colors.teal.shade100,
    onSecondaryContainer: Colors.teal.shade900,
    tertiary: Colors.orange,
    onTertiary: Colors.white,
    error: Colors.red,
    onError: Colors.white,
    surface: Colors.white,
    onSurface: Colors.black,
  ),
)

2.3 使用颜色 #

dart
Container(
  color: Theme.of(context).colorScheme.primary,
)

Container(
  color: Theme.of(context).colorScheme.secondary,
)

Container(
  color: Theme.of(context).colorScheme.error,
)

三、TextTheme #

3.1 文本样式 #

dart
ThemeData(
  textTheme: TextTheme(
    displayLarge: TextStyle(fontSize: 57, fontWeight: FontWeight.w400),
    displayMedium: TextStyle(fontSize: 45, fontWeight: FontWeight.w400),
    displaySmall: TextStyle(fontSize: 36, fontWeight: FontWeight.w400),
    headlineLarge: TextStyle(fontSize: 32, fontWeight: FontWeight.w400),
    headlineMedium: TextStyle(fontSize: 28, fontWeight: FontWeight.w400),
    headlineSmall: TextStyle(fontSize: 24, fontWeight: FontWeight.w400),
    titleLarge: TextStyle(fontSize: 22, fontWeight: FontWeight.w400),
    titleMedium: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
    titleSmall: TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
    bodyLarge: TextStyle(fontSize: 16, fontWeight: FontWeight.w400),
    bodyMedium: TextStyle(fontSize: 14, fontWeight: FontWeight.w400),
    bodySmall: TextStyle(fontSize: 12, fontWeight: FontWeight.w400),
    labelLarge: TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
    labelMedium: TextStyle(fontSize: 12, fontWeight: FontWeight.w500),
    labelSmall: TextStyle(fontSize: 11, fontWeight: FontWeight.w500),
  ),
)

3.2 使用文本样式 #

dart
Text(
  'Title',
  style: Theme.of(context).textTheme.titleLarge,
)

Text(
  'Body text',
  style: Theme.of(context).textTheme.bodyMedium,
)

四、暗黑模式 #

4.1 配置暗黑主题 #

dart
MaterialApp(
  theme: ThemeData(
    colorScheme: ColorScheme.fromSeed(
      seedColor: Colors.blue,
      brightness: Brightness.light,
    ),
    useMaterial3: true,
  ),
  darkTheme: ThemeData(
    colorScheme: ColorScheme.fromSeed(
      seedColor: Colors.blue,
      brightness: Brightness.dark,
    ),
    useMaterial3: true,
  ),
  themeMode: ThemeMode.system,
)

4.2 ThemeMode #

dart
ThemeMode.system
ThemeMode.light
ThemeMode.dark

4.3 动态切换主题 #

dart
class ThemeProvider extends ChangeNotifier {
  ThemeMode _themeMode = ThemeMode.system;
  
  ThemeMode get themeMode => _themeMode;
  
  void setThemeMode(ThemeMode mode) {
    _themeMode = mode;
    notifyListeners();
  }
  
  void toggleTheme() {
    _themeMode = _themeMode == ThemeMode.light 
        ? ThemeMode.dark 
        : ThemeMode.light;
    notifyListeners();
  }
}

MaterialApp(
  themeMode: context.watch<ThemeProvider>().themeMode,
  theme: ThemeData.light(),
  darkTheme: ThemeData.dark(),
)

五、Theme Widget #

5.1 局部主题 #

dart
Theme(
  data: Theme.of(context).copyWith(
    primaryColor: Colors.red,
  ),
  child: Builder(
    builder: (context) {
      return Container(
        color: Theme.of(context).primaryColor,
      );
    },
  ),
)

5.2 获取主题 #

dart
final theme = Theme.of(context);
final colorScheme = Theme.of(context).colorScheme;
final textTheme = Theme.of(context).textTheme;

六、自定义主题扩展 #

6.1 ThemeExtension #

dart
class MyColors extends ThemeExtension<MyColors> {
  final Color? brand;
  final Color? accent;
  
  MyColors({
    required this.brand,
    required this.accent,
  });
  
  @override
  MyColors copyWith({
    Color? brand,
    Color? accent,
  }) {
    return MyColors(
      brand: brand ?? this.brand,
      accent: accent ?? this.accent,
    );
  }
  
  @override
  MyColors lerp(MyColors? other, double t) {
    if (other is! MyColors) {
      return this;
    }
    return MyColors(
      brand: Color.lerp(brand, other.brand, t),
      accent: Color.lerp(accent, other.accent, t),
    );
  }
}

ThemeData(
  extensions: [
    MyColors(
      brand: Colors.purple,
      accent: Colors.orange,
    ),
  ],
)

final myColors = Theme.of(context).extension<MyColors>()!;

七、总结 #

7.1 核心概念 #

概念 说明
ThemeData 主题配置
ColorScheme 颜色方案
TextTheme 文本样式
ThemeMode 主题模式

7.2 下一步 #

掌握了样式与主题后,让我们学习 状态管理概述

最后更新:2026-03-28