使用 Themes 統一顏色和字型風格
你可以使用主題來全域應用顏色和文字樣式。
你可以定義應用全域的主題。你也可以為某一個元件單獨繼承一個特定的主題。每個主題都可以各自定義顏色、文字樣式和其他 Material 設定引數。
Flutter 會按以下順序應用樣式:
-
針對特定 widget 的樣式。
-
過載的繼承主題的樣式。
-
應用的總體樣式。
在定義一個 Theme
之後,我們可以讓它在指定的 widgets,包括 Flutter 自帶的 Material widgets,例如
AppBars、Buttons、Checkboxes 等 widget 中生效。
Create an app theme
全域 Theme 會影響整個 app 的顏色和字型樣式。只需要向 MaterialApp
構造器傳入 ThemeData
即可。
從 Flutter 3.16 版本開始, Material 3 是 Flutter 的預設主題。
如果沒有手動設定主題,Flutter 將會使用預設的樣式。
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
實例會設定以下兩個屬性。它們會影響大部分樣式屬性。
-
colorScheme
定義了顏色。 -
textTheme
定義了文字樣式。
你可以在 ThemeData
文件中檢視所有可自定義的顏色和字型樣式。
應用指定的主題
要想應用你的主題,使用 Theme.of(context)
方法來指定 widget 的樣式屬性。其包括但不限於樣式和顏色。
Theme.of(context)
會查詢 widget 樹,並回傳其中最近的 Theme
。所以他會優先回傳我們之前定義過的一個獨立的 Theme
,如果找不到,它會回傳全域主題。
在下面的例子中,Container
的顏色使用的就是指定主題(上層)的顏色。
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,
),
),
),
Override a theme
你可以用 Theme
widget 巢狀想要改變主題的部分以進行主題過載。
以下是兩種過載主題的方法:
-
構造一個不一樣的
ThemeData
實例。 -
繼承上層主題。
ThemeData
instance
Set a unique 如果不想從任何全域 Theme 繼承樣式,我們可以建立一個 ThemeData()
實例,然後把它傳給 Theme
widget:
Theme(
// Create a unique theme with `ThemeData`.
data: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.pink,
),
),
child: FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.add),
),
);
Extend the parent theme
相比從頭開始定義一套樣式,從上層 Theme 擴充套件可能更常規一些,使用 copyWith()
方法即可。
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
的相關影片
觀看 想要瞭解更多,你可以觀看 Widget of the Week 中關於 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),
),
),
);
}
}