使用主题共享颜色和字体样式

要在一个应用程序中共享颜色和字体样式,请使用主题。

您可以定义应用范围的主题。您可以扩展主题以更改一个组件的主题样式。每个主题都定义了适用于 Material 组件类型的颜色、类型样式和其他参数。

Flutter 按以下顺序应用样式

  1. 应用于特定小部件的样式。
  2. 覆盖直接父主题的主题。
  3. 整个应用程序的主主题。

定义 Theme 后,在您自己的小部件中使用它。Flutter 的 Material 小部件使用您的主题来设置应用栏、按钮、复选框等的背景颜色和字体样式。

创建应用程序主题

#

要在整个应用程序中共享 Theme,请将 theme 属性设置为 MaterialApp 构造函数。此属性接受一个 ThemeData 实例。

从 Flutter 3.16 版本开始,Material 3 是 Flutter 的默认主题。

如果您在构造函数中未指定主题,Flutter 会为您创建一个默认主题。

dart
MaterialApp(
  title: appName,
  theme: ThemeData(
    useMaterial3: true,

    // Define the default brightness and colors.
    colorScheme: ColorScheme.fromSeed(
      seedColor: Colors.purple,
      // ···
      brightness: Brightness.dark,
    ),

    // Define the default `TextTheme`. Use this to specify the default
    // text styling for headlines, titles, bodies of text, and more.
    textTheme: TextTheme(
      displayLarge: const TextStyle(
        fontSize: 72,
        fontWeight: FontWeight.bold,
      ),
      // ···
      titleLarge: GoogleFonts.oswald(
        fontSize: 30,
        fontStyle: FontStyle.italic,
      ),
      bodyMedium: GoogleFonts.merriweather(),
      displaySmall: GoogleFonts.pacifico(),
    ),
  ),
  home: const MyHomePage(
    title: appName,
  ),
);

大多数 ThemeData 实例都为以下两个属性设置值。这些属性会影响整个应用程序。

  1. colorScheme 定义颜色。
  2. textTheme 定义文本样式。

要了解您可以定义的颜色、字体和其他属性,请查看 ThemeData 文档。

应用主题

#

要应用新主题,请在指定小部件的样式属性时使用 Theme.of(context) 方法。这些属性可能包括但不限于 stylecolor

Theme.of(context) 方法会查找小部件树并检索树中最接近的 Theme。如果您有一个独立的 Theme,则会应用该主题。如果没有,Flutter 会应用应用程序的主题。

在以下示例中,Container 构造函数使用此技术来设置其 color

dart
Container(
  padding: const EdgeInsets.symmetric(
    horizontal: 12,
    vertical: 12,
  ),
  color: Theme.of(context).colorScheme.primary,
  child: Text(
    'Text with a background color',
    // ···
    style: Theme.of(context).textTheme.bodyMedium!.copyWith(
          color: Theme.of(context).colorScheme.onPrimary,
        ),
  ),
),

覆盖主题

#

要在应用程序的一部分中覆盖整体主题,请将该部分应用程序包装在 Theme 小部件中。

您可以通过两种方式覆盖主题

  1. 创建一个唯一的 ThemeData 实例。
  2. 扩展父主题。

设置一个唯一的 ThemeData 实例

#

如果您希望应用程序中的某个组件忽略全局主题,请创建一个 ThemeData 实例。将该实例传递给 Theme 小部件。

dart
Theme(
  // Create a unique theme with `ThemeData`.
  data: ThemeData(
    colorScheme: ColorScheme.fromSeed(
      seedColor: Colors.pink,
    ),
  ),
  child: FloatingActionButton(
    onPressed: () {},
    child: const Icon(Icons.add),
  ),
);

扩展父主题

#

与其覆盖所有内容,不如考虑扩展父主题。要扩展主题,请使用 copyWith() 方法。

dart
Theme(
  // Find and extend the parent theme using `copyWith`.
  // To learn more, check out the section on `Theme.of`.
  data: Theme.of(context).copyWith(
    colorScheme: ColorScheme.fromSeed(
      seedColor: Colors.pink,
    ),
  ),
  child: const FloatingActionButton(
    onPressed: null,
    child: Icon(Icons.add),
  ),
);

观看关于 Theme 的视频

#

要了解更多信息,请观看有关 Theme 小部件的这个简短的“本周小部件”视频

尝试交互式示例

#
import 'package:flutter/material.dart';
// Include the Google Fonts package to provide more text format options
// https://pub.dev/packages/google_fonts
import 'package:google_fonts/google_fonts.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    const appName = 'Custom Themes';

    return MaterialApp(
      title: appName,
      theme: ThemeData(
        useMaterial3: true,

        // Define the default brightness and colors.
        colorScheme: ColorScheme.fromSeed(
          seedColor: Colors.purple,
          // TRY THIS: Change to "Brightness.light"
          //           and see that all colors change
          //           to better contrast a light background.
          brightness: Brightness.dark,
        ),

        // Define the default `TextTheme`. Use this to specify the default
        // text styling for headlines, titles, bodies of text, and more.
        textTheme: TextTheme(
          displayLarge: const TextStyle(
            fontSize: 72,
            fontWeight: FontWeight.bold,
          ),
          // TRY THIS: Change one of the GoogleFonts
          //           to "lato", "poppins", or "lora".
          //           The title uses "titleLarge"
          //           and the middle text uses "bodyMedium".
          titleLarge: GoogleFonts.oswald(
            fontSize: 30,
            fontStyle: FontStyle.italic,
          ),
          bodyMedium: GoogleFonts.merriweather(),
          displaySmall: GoogleFonts.pacifico(),
        ),
      ),
      home: const MyHomePage(
        title: appName,
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  final String title;

  const MyHomePage({super.key, required this.title});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title,
            style: Theme.of(context).textTheme.titleLarge!.copyWith(
                  color: Theme.of(context).colorScheme.onSecondary,
                )),
        backgroundColor: Theme.of(context).colorScheme.secondary,
      ),
      body: Center(
        child: Container(
          padding: const EdgeInsets.symmetric(
            horizontal: 12,
            vertical: 12,
          ),
          color: Theme.of(context).colorScheme.primary,
          child: Text(
            'Text with a background color',
            // TRY THIS: Change the Text value
            //           or change the Theme.of(context).textTheme
            //           to "displayLarge" or "displaySmall".
            style: Theme.of(context).textTheme.bodyMedium!.copyWith(
                  color: Theme.of(context).colorScheme.onPrimary,
                ),
          ),
        ),
      ),
      floatingActionButton: Theme(
        data: Theme.of(context).copyWith(
          // TRY THIS: Change the seedColor to "Colors.red" or
          //           "Colors.blue".
          colorScheme: ColorScheme.fromSeed(
            seedColor: Colors.pink,
            brightness: Brightness.dark,
          ),
        ),
        child: FloatingActionButton(
          onPressed: () {},
          child: const Icon(Icons.add),
        ),
      ),
    );
  }
}