【Flutter】ダークモード対応方法まとめ(固定/自動切替/任意切替)

Flutter

Flutter のダークモード対応方法3種類、①ダークテーマ固定②デバイスのモード設定に合わせて自動切り替え③任意のテーマに手動で切り替え、について解説します。

①②は簡単なので、③の解説がメインな感じになっています。

スポンサーリンク

ダークテーマ固定

MaterialApptheme プロパティにダークテーマを指定すれば、ダークテーマ固定になります。

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData.dark(), //← これでダークテーマ固定
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

デバイスのモード設定に合わせて自動切り替え

MaterialApp ウィジェットの、

  • theme プロパティにライト用テーマを設定
  • darkTheme プロパティにダーク用テーマを設定
  • themeMode プロパティを ThemeMode.system に設定

以上でデバイスのモード設定に応じて自動的にテーマが切り替わります。

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData.light(), // ライト用テーマ
      darkTheme: ThemeData.dark(), // ダーク用テーマ
      themeMode: ThemeMode.system, // モードをシステム設定にする
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

任意のテーマに手動で切り替え

ここで解説するのは最も柔軟な方法です。任意のテーマを複数定義して、いつでも自由にそれらのテーマを切り替える事ができます。

provider パッケージを追加

provider パッケージを使うので、pubspec.yaml に追加します。

今回 provider の説明は省きますが、簡単に説明すると任意のオブジェクトをウイジェット間で自由に参照できたり、値の更新を監視できたりするもので、専門的には、DI【 Dependency Injection 】  依存性注入、と呼ばれるデザインパターンを実現するものです。

※ provider は Flutter 公式も利用を推奨しているパッケージです。

dependencies:
  flutter:
    sdk: flutter
  # ↓これを追加
  provider: ^4.0.2

ソースコードでインポートするファイルはこちら:

import 'package:provider/provider.dart';

テーマ変更用の状態クラスを定義

Provider で利用するテーマ変更用の状態クラスを定義します。

// テーマ変更用の状態クラス
class MyTheme extends ChangeNotifier {
  ThemeData current = ThemeData.light();
  bool _isDark = false;

  // とりあえずトグルでテーマを切り替える関数だけ定義しています
  toggle() {
    _isDark = !_isDark;
    current = _isDark ? ThemeData.dark() : ThemeData.light();
    notifyListeners();
  }
}

使い方

ChangeNotifierProvider の中にウイジェットを入れて、MaterialApp クラスの theme プロパティで MyTheme のカレントテーマを参照するようにします。

// MyApp ウイジェット
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => MyTheme(),
      child: Consumer<MyTheme>(
        builder: (context, theme, _) {
          return MaterialApp(
            theme: theme.current,
            home: MyHomePage(title: 'Example'),
          );
        },
      ),
    );
  }
}
  • 画面全体更新なので Consumer を省いても同じ動作になります。
  • もっと上の runApp() に ChangeNotifierProvider を書いても良いです。

テーマを変更したい場所で、MyThemeクラスの toggle 関数を呼び出せば、テーマが切り替わります。

Provider.of<MyTheme>(context, listen: false).toggle();

サンプルコード

全体のサンプルコードです。

右下のフローティングボタンを押すと、トグルでライトとダークのテーマが切り替わります。

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

// テーマ変更用の状態クラス
class MyTheme extends ChangeNotifier {
  ThemeData current = ThemeData.light();
  bool _isDark = false;

  toggle() {
    _isDark = !_isDark;
    current = _isDark ? ThemeData.dark() : ThemeData.light();
    notifyListeners();
  }
}

void main() => runApp(MyApp());

// MyApp ウイジェット
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => MyTheme(),
      child: Consumer<MyTheme>(
        builder: (context, theme, _) {
          return MaterialApp(
            theme: theme.current,
            home: MyHomePage(title: 'Example'),
          );
        },
      ),
    );
  }
}

// MyHomePage ウイジェット
class MyHomePage extends StatelessWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('テーマ切替テスト'),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed:  () {
          Provider.of<MyTheme>(context, listen: false).toggle();
        },
        child: Icon(Icons.autorenew),
      ),
    );
  }
}

サンプルではとりあえず toggle 関数を書きましたが、MyTheme クラスの中身を書き換えれば任意のテーマを自由に選択するような感じにも簡単にできると思います。

ちなみに、provider 経由の UI 更新なので(setState は不要)StatelessWidget のみで構成しています。

コメント

タイトルとURLをコピーしました