色とタイポグラフィをThemeDataに集約し、ColorScheme.fromSeedでMaterial 3のパレットを生成し、労せずにダークモードを提供しましょう。
このレッスンを Kodokon で開くウィジェットごとにスタイルを付けるのをやめることは、プロとしての節目です。ThemeDataはアプリ全体の色、タイポグラフィ、形状を集約します。Material 3(Flutter 3.16以降デフォルトで有効)では、すべてがColorSchemeから始まります。ColorScheme.fromSeedは、たった一つのシード色から、完全で調和のとれた、アクセシブルなパレットを導き出します。
import 'package:flutter/material.dart';
void main() {
runApp(const ShopApp());
}
class ShopApp extends StatelessWidget {
const ShopApp({super.key});
@override
Widget build(BuildContext context) {
const seed = Colors.teal;
return MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: seed,
),
),
darkTheme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: seed,
brightness: Brightness.dark,
),
),
themeMode: ThemeMode.system,
home: Scaffold(
appBar: AppBar(title: const Text('Shop')),
body: const Center(child: Text('Welcome')),
),
);
}
}ウィジェットの中では、何も作り直さないでください。Theme.of(context)で周囲のテーマを読み取りましょう。colorSchemeは意味を持つ役割(primary、onPrimary、primaryContainer、surfaceなど)を公開し、textThemeは名前の付いたスタイル(titleMedium、bodyLargeなど)を公開します。このように書かれたウィジェットは、ライトテーマにもダークテーマにも自動的に適応します。
import 'package:flutter/material.dart';
class PriceTag extends StatelessWidget {
const PriceTag({super.key, required this.price});
final double price;
@override
Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme;
final text = Theme.of(context).textTheme;
return Container(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
),
decoration: BoxDecoration(
color: colors.primaryContainer,
borderRadius: BorderRadius.circular(8),
),
child: Text(
'$price €',
style: text.titleMedium?.copyWith(
color: colors.onPrimaryContainer,
),
),
);
}
}さらに進むには、コンポーネントテーマを設定しましょう。ウィジェットの各ファミリー(アプリバー、ボタン、入力フィールド)は、ThemeDataの中で専用のテーマを受け付けます。ボタンのパディングやタイトルの中央寄せを一度定義すれば、すべてのインスタンスが、ローカルなスタイルを一つも書かずにそれを継承します。
import 'package:flutter/material.dart';
final ThemeData appTheme = ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.teal,
),
appBarTheme: const AppBarTheme(
centerTitle: true,
elevation: 0,
),
filledButtonTheme: FilledButtonThemeData(
style: FilledButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 24,
vertical: 12,
),
),
),
);