跳到主內容

使用主題共享顏色和字型樣式

如何使用主題在整個應用中共享顏色和字型樣式。

要實現在整個應用中共享顏色和字型樣式,請使用主題。

你可以定義全域性應用主題。你也可以擴充套件主題以更改單個元件的主題樣式。每個主題都定義了適用於相應 Material 元件型別的顏色、文字樣式及其他引數。

Flutter 按以下順序應用樣式:

  1. 應用於特定 Widget 的樣式。
  2. 覆蓋直接父級主題的主題。
  3. 整個應用的主題。

定義 Theme 後,可在你自己的 Widget 中使用它。Flutter 的 Material Widget 會使用你的主題來設定應用欄、按鈕、複選框等的背景顏色和字型樣式。

建立應用主題

#

要在整個應用中共享 Theme,請將 theme 屬性設定為 MaterialApp 的建構函式。該屬性接收一個 ThemeData 例項。

如果你沒有在建構函式中指定主題,Flutter 會為你建立一個預設主題。

dart
MaterialApp(
  title: appName,
  theme: ThemeData(
    // 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 文件。

應用主題

#

要應用你的新主題,請在指定 Widget 的樣式屬性時使用 Theme.of(context) 方法。這些屬性可以包括(但不限於)stylecolor

Theme.of(context) 方法會向上查詢 Widget 樹,並檢索樹中最近的 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 Widget 中。

你可以透過兩種方式覆蓋主題:

  1. 建立一個唯一的 ThemeData 例項。
  2. 擴充套件父主題。

設定唯一的 ThemeData 例項

#

如果你希望應用的某個元件忽略整體主題,請建立一個 ThemeData 例項,並將該例項傳遞給 Theme Widget。

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 Widget 的簡短“每週元件”(Widget of the Week)影片。

在 YouTube 新標籤頁中觀看:“Theme | Flutter 每週元件”

嘗試互動式示例

#
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(
        // 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),
        ),
      ),
    );
  }
}