search
LoginSignup
0

More than 1 year has passed since last update.

posted at

FlutterのThemeをBlocを用いて切り替える

Flutterにて複数のThemeをUIから切り替える方法について、この記事を紹介することで説明します。

概要

以下のようにBlocを用いた状態管理によりThemeDataを変更します。

  • 複数のThemeDataを定義しておく
  • ThemeDataを切り替えるBlocを作成する
    • ThemeDataを切り替えるButtonを押した際にEventをBlocに送る
    • BlocがThemeDataを持つStateをViewに通知する
  • MaterialAppのthemeプロパティがStateのThemeDataを参照する

複数のThemeDataを定義しておく

このサンプルでは以下の4種類のThemeを切り替えます。お好みで増やすことも可能です。
ThemeDataのプロパティも必要に応じて増やしてください。

// ui/global/app_themes.dart
enum AppTheme {
  GreenLight,
  GreenDark,
  BlueLight,
  BlueDark,
}

// 4つのTheeDataを作成する
final appThemeData = {
  AppTheme.GreenLight: ThemeData(
    brightness: Brightness.light,
    primaryColor: Colors.green,
  ),
  // 省略
}

ThemeDataを切り替えるBlocを作成する

theme_event.dart
// 抜粋
// 注: 抽象クラスThemeEventはEquatableをextendsしている
// 4つのAppTemeのうちで選択されたものをBlocに通知する動きをする
class ThemeChanged extends ThemeEvent {
  final AppTheme theme;

  ThemeChanged({
    @required this.theme,
  }) : super([theme]);
}
theme_state.dart
// 抜粋
// 選択されたAppThemeに基づくThemeDataをUIに通知する動きをする
@immutable
class ThemeState extends Equatable {
  final ThemeData themeData;

  ThemeState({
    @required this.themeData,
  }) : super([themeData]);
}
theme_bloc.dart
// 抜粋
class ThemeBloc extends Bloc<ThemeEvent, ThemeState> {
  ThemeBloc(ThemeState initialState) : super(initialState);

  @override
  Stream<ThemeState> mapEventToState(
    ThemeEvent event,
  ) async* {
    if (event is ThemeChanged) {
      // Eventで受け取ったAppThemeに基づくThemeDataを通知する
      yield ThemeState(themeData: appThemeData[event.theme]);
    }
  }
}

ThemeDataを切り替えるButtonを押した際にEventをBlocに送る

preference_page.dart
// 抜粋
return Card(
  color: appThemeData[itemAppTheme].primaryColor,
  child: ListTile(
    title: Text(
      itemAppTheme.toString(),
      style: appThemeData[itemAppTheme].textTheme.bodyText2,
    ),
    onTap: () {
      // 選択されたAppThemeをBlocに流す
      // 
      BlocProvider.of<ThemeBloc>(context).add(
        ThemeChanged(theme: itemAppTheme),
      );
    },
  ),
);

MaterialAppのthemeプロパティがStateのThemeDataを参照する

main.dart
// 抜粋
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // BlocProviderでThemeBlocをWidget tree挿入する
    return BlocProvider(
      create: (BuildContext context) => ThemeBloc(
        // 初期値を設定する
        ThemeState(themeData: appThemeData[AppTheme.GreenLight]),
      ),
      child: BlocBuilder<ThemeBloc, ThemeState>(
        builder: _buildWithTheme,
      ),
    );
  }

  Widget _buildWithTheme(BuildContext context, ThemeState state) {
    return MaterialApp(
      title: 'Material App',
      // themeとしてStateのthemeDataを反映させる
      theme: state.themeData,
      home: HomePage(),
    );
  }
}
home_page.dart
// 抜粋
body: Center(
  child: Container(
    child: Text(
      'Home',
      // styleとしてBlocから通知されたStateのthemeを呼び出す
      style: Theme.of(context).textTheme.headline4,
    ),
  ),
),

まとめ

flutter_blocを用いた状態管理によりThemeDataを変更しました。
完成したコードはこちらのresocoderさんの記事にありますので参照してください。

resocoderさんのコードではThemeDataは永続化されませんので、必要に応じて永続化をしてください。記事ではpreferences、SEMBAST、Moor等を勧めています。

またresocoderさんの記事ではflutter_blocのバージョンが古いものになっています。
私の書いた記事はflutter_bloc6.1.1を用いて書いたものになります。適宜バージョンにあった形に直してお使いください。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
0