0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

わたしのFlutterお勉強用Advent Calendar 2020

Day 13

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

Posted at

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を用いて書いたものになります。適宜バージョンにあった形に直してお使いください。

0
0
0

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
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?