どうも、たくチャレ(@takuchalle)です。

Flutterproviderパッケージを使ってものすごく簡単にダークモードの対応する方法を紹介します。

ツイートしたけど、ブログにもまとめておきます。

リビルド範囲が云々って書いたけど、よく考えたらダークモードに切り替えた場合画面全体がリビルドされるから関係なかったです。

OS の設定に連動してダークモードに切り替わる方法も書いたので、こちらも参考にしてみてください。

Flutterのダークモード対応

FlutterMaterialApp Widgetthemeにテーマの定義を行います。ここでアプリに合わせた色や文字サイズを定義することで、アプリ全体の統一感を出すことができます。

ThemeDataのコンストラクタにbrightnessがあり、ここにBrightness.darkを指定するとアプリ全体がダークモードになります。

MaterialApp(
	theme: ThemeData(brightness: Brightness.dark),
	home: Scaffold(),
)

めちゃめちゃ簡単ですね。

しかし、通常 MaterialAppは Widget ツリーの上位にあるので、設定画面などから変更しようとすると悩むかもしれません。

そこでみんな大好きproviderパッケージを使えばものすごく簡単に対応することができます。providerパッケージ自体の説明はしないので適宜調べてください。

providerパッケージでダークモード対応

実際のサンプルコードを交えて説明します。今回のサンプルの要件はシンプルに以下の様にします。

  • 初期状態は通常モード
  • 設定画面などから通常モード・ダークモードを切り替えられる

テーマ管理クラス作成する

まずテーマを管理するクラスをChangeNotifierを継承して作成します。ここではAppThemeと命名します。

class AppTheme extends ChangeNotifier {
	AppTheme() : _isDark = false;

	bool get isDark => _isDark;

	bool _isDark;
	
	ThemeData buildTheme()
		=> _isDark ? ThemeData.dark() : ThemeData.light();
	
	void changeMode() {
		_isDark = !_isDark;
		notifyListeners();
	}
} 

初期値は通常モードにしておきます。isDarkゲッタで現在のモードを返します。 buildThemeメソッドは現在のモードに合わせたThemeDataを返します。 今回はデフォルト値を返していますが、必要に応じてここでアプリに合わせてテーマを定義してください。 changeThemeModeメソッドを呼ぶとモードが切り替わり、切り替わったことを子Widgetに伝えます。

テーマを適用する

main関数のrunAppの中でアプリ全体を囲う形でChangeNotifierProviderを使います。これでアプリのどこからでもAppThemeを参照することができます。

void main() => runApp(
	ChangeNotifierProvider(
		create: (_) => AppTheme(),
		child: const App(),
	),
);

MaterialAppで先ほどのAppThemeを参照してテーマを適用します。初期状態は通常モードなので、ThemeData.light()が適用されています。

Widget build(BuildContext context) {
	return MaterialApp(
		theme: Provider.of<AppTheme>(context).buildTheme(),
		home: Scaffold(),
	);
)

テーマを変更する

おそらくユーザの設定画面でダークモードの切り替えを行うことが多いと思います。 通常モードとダークモードのbool値の切り替えなのでSwitch Widgetを使います。

buildメソッドで最初に定義したAppThemeを取得し、

final theme = Provider.of<AppTheme>(context);

Switch WidgetvalueonChangedを以下のように設定してあげると完成です。状態はAppThemeが持っているので、ここではStatelessWidgetで十分です。

Switch.adaptive(
	value: theme.isDark,
	onChanged: (_) {
		theme.changeMode();
	},
),

スイッチを切り替える度に変更が伝えられてMaterialAppProvider.of<AppTheme>(context).buildTheme()が呼び出されて、画面全体が書き換わります。

まとめ

providerを使って簡単にダークモードに対応する方法を紹介しました。

今回は通常モードとダークモードの切り替えでしたが、複数のテーマ切り替えにも応用できるテクニックだと思います。

コード全体を確認したり、動きを確認したい場合はGitHubdark_mode_sampleがありますので、参考にしてみてください。