Kodokon kodokon.com

テーマ:ThemeData、Material 3、ダークモード

色とタイポグラフィをThemeDataに集約し、ColorScheme.fromSeedでMaterial 3のパレットを生成し、労せずにダークモードを提供しましょう。

9 分 · 3 問

このレッスンを Kodokon で開く

ウィジェットごとにスタイルを付けるのをやめることは、プロとしての節目です。ThemeDataはアプリ全体の色、タイポグラフィ、形状を集約します。Material 3(Flutter 3.16以降デフォルトで有効)では、すべてがColorSchemeから始まります。ColorScheme.fromSeedは、たった一つのシード色から、完全で調和のとれた、アクセシブルなパレットを導き出します。

DART
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は意味を持つ役割primaryonPrimaryprimaryContainersurfaceなど)を公開し、textThemeは名前の付いたスタイル(titleMediumbodyLargeなど)を公開します。このように書かれたウィジェットは、ライトテーマにもダークテーマにも自動的に適応します。

DART
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の中で専用のテーマを受け付けます。ボタンのパディングやタイトルの中央寄せを一度定義すれば、すべてのインスタンスが、ローカルなスタイルを一つも書かずにそれを継承します。

DART
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,
      ),
    ),
  ),
);
コンポーネントの設定はアプリ全体に適用される

理解度チェック

このレッスンの要点をしっかり覚えているか確認しましょう。

  1. ColorScheme.fromSeed(seedColor: ...)は何を生み出しますか?
    • 少しだけ明るくされた一つの色
    • シード色から導かれた、完全で首尾一貫したMaterial 3のパレット
    • 背景用のグラデーション
    • ダークテーマだけ
  2. アプリをシステムのライト/ダーク設定に従わせるにはどうしますか?
    • themeだけを指定する。あとは自動
    • themeとdarkThemeを指定し、themeModeをThemeMode.systemに設定する
    • 明るさが変わったときにsetStateを呼ぶ
    • すべてのウィジェットでMediaQueryを読む
  3. なぜcolor: Colors.blueではなくcolor: Theme.of(context).colorScheme.primaryと書くのですか?
    • 書くのが短くて済む
    • 色が有効なテーマに適応し、アプリ全体で一貫性を保つ
    • Colors.blueはMaterial 3では非推奨である