実現したいこと
こんな感じのことをしたい
説明とかいいからサンプルよこせという方はこちらから。cloneして実行して実際にいじりながら試してみてください。
ライブラリ等を探してみた
とりあえず探してみました。すると、
いい感じの記事を見つけた!しかも配布されている😊
これで終わった〜😊 と、思ったらちょっと機能が足りませんでした。
このライブラリができることは
- 動的に
brightness(明暗)
を変更できる - 変更した
brightness
はshared_preferences
に保存される - 次回起動時に自動的に保存した
brightness
が適用される
というものでした。しかし、今自分がやりたいことはbrightness
だけでなく、primarySwatch
も変更/保存したい、ということだったため少し機能不足でした。
FlutterのThemeに関する補足
ここで急にbrightness
とかprimarySwatch
とか出てきて、チョ・マテヨ👩ってなってる人に向けて補足説明をします。自分も最初FlutterでThemeを扱ったとき、どういうクラス構造になっているのか分からず道に迷ってしまいました。ThemeData
, primarySwatch
, brightness
, MaterialColor
等のクラスや引数の関係性がだいたい分かっている方は読み飛ばしてOKだと思います。
まず、Flutterで起動時にテーマを指定するサンプルコードを見てみましょう。
return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, brightness: Brightness.light, ), home: SomePage(), );
こう書くと、アプリ全体が青い配色になって、背景は白くなります。
上記サンプルコードではtheme
というパラメータにThemeData
オブジェクトを渡しているのが分かると思います。これを見ながら語句の解説をしましょう。
解説 | |
---|---|
ThemeData | Flutterのデザインテーマのデータを全て持つ。明るさ、プライマリカラー、アクセントカラーやその他ボタンの色やAppBarの色なんかも持っている |
primarySwatch | 一言で言えば、「色見本」(swatchは見本という意味)。型はMaterialColor 。 |
brightness | 明るさ。これもThemeData のコンストラクタに渡すことができる。lightとdarkしかない。 |
MaterialColor | クラス。dartにはColor というクラスもあるが、それとは別。Color は1つの色しか保持できないのに対し、MaterialColor は「同じ系統の複数の色」(濃い青、普通の青、薄い青等)を保持できる。 |
もう少し付け加えると、まずThemeData
のコンストラクタには直接primaryColor
やaccentColor
等が渡せるようにもなっています。しかし、このprimarySwatch
を渡しておけばそれらの値はprimarySwatch
から取り出されます。
また、上記説明から分かるとは思いますが、引数primarySwatch
の値はColors.blue
で、このColors.blue
の型はMaterialColor
です。つまり、blueと言っても、実際には濃い青から薄い青まで様々な値を保持しています。
ここでThemeDataの特徴なのですが、 brightnessがdarkの場合はprimarySwatchが何であろうとダークテーマになります。先程のソースコードを見てもらえれば分かると思いますが、例えばprimaryColor
に関しては
primaryColor ??= isDark ? Colors.grey[900] : primarySwatch;
となっていて、darkのときはColors.grey[900]
が適用されるようになっています。primarySwatch
がblueであろうとredであろうと関係ないというわけです。
カスタマイズする
見つけたライブラリではうまくprimarySwatchを変更/保持できなかったのでそれができるように変更していきます。見つけたライブラリですが、実は中身はdynamic_theme.dart
1ファイルだけという非常にシンプルな作りになっています。
今回はこのライブラリのソースをまるっと自分のプロジェクト内にコピーして好きなように変更していきます。要点だけ説明します。
主な変更箇所
1. L34 shared preferenceのキー
static const String _sharedPreferencesKey = 'isDark';
brightnessだけを保存するのではなく、テーマ名を保存することにするので、このkeyは変更します
static const String _themeTypeKey = 'theme_type';
2. L67 setBrightnessメソッド
setBrightnessからsetTheme
に変更します。
void setTheme(ThemeType theme) async { setState(() { this._data = themeMap[theme]; }); SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.setString(_themeTypeKey, theme.toString()); }
3. L83 loadBrightnessメソッド
loadBrightnessからsetTheme
に変更します。
Future<ThemeType> loadThemeType() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
return (ThemeType.of(prefs.getString(_themeTypeKey)) ?? ThemeType.BLUE);
}
フォルダ構成
今回は、dynaic_theme.dart
の他、テーマの種類を保持するenumのようなクラスであるtheme_type.dart
と実際のテーマの色やbrightnessを定義したapp_theme.dart
を追加します。サンプルでは、home画面でテーマを選択できるRadioListTileのリストを表示するので、そのためのtheme_select.dart
も追加します。その結果、フォルダ構成は以下のようになります。
lib/ ┣ theme/ ┃ ┣ app_theme.dart ┃ ┣ dynamic_theme.dart ┃ ┗ theme_type.dart ┣━ main.dart ┗━ theme_select.dart
使い方
使い方は本家dynamic_themeと変わりません。MaterialAppを生成するときにラップしてあげてください。
return DynamicTheme( themedWidgetBuilder: (context, theme) { return MaterialApp( title: 'Flutter Theme Example', theme: theme, home: ThemeSelectPage(), ); }, );
ピンク濃すぎ問題
さて、ここで問題が発生しました。app_theme.dart
に様々なMaterialColorでテーマを定義したのですが、なんかピンク(Colors.pink)が濃い...
ということで、独自のピンク色のテーマを定義することにしました。元のピンク色の定義はこんな感じでした。
static const MaterialColor pink = MaterialColor( _pinkPrimaryValue, <int, Color>{ 50: Color(0xFFFCE4EC), 100: Color(0xFFF8BBD0), 200: Color(0xFFF48FB1), 300: Color(0xFFF06292), 400: Color(0xFFEC407A), 500: Color(_pinkPrimaryValue), 600: Color(0xFFD81B60), 700: Color(0xFFC2185B), 800: Color(0xFFAD1457), 900: Color(0xFF880E4F), }, );
50の方が徐々に薄くなるようグラデーションをつけて色が定義されています。今回は、最も濃くても700くらいの濃さがあれば十分だと思ったので元の700を900に使って以下のようにあらたなピンク色を定義します。50,100,200が同じ色になってしまっていますが、単なる妥協です😊
static const int _pinkPrimaryValue = 0xFFF06292; static const MaterialColor softPink = MaterialColor( _pinkPrimaryValue, <int, Color>{ 50: Color(0xFFFCE4EC), 100: Color(0xFFFCE4EC), 200: Color(0xFFFCE4EC), 300: Color(0xFFF8BBD0), 400: Color(0xFFF48FB1), 500: Color(_pinkPrimaryValue), 600: Color(0xFFEC407A), 700: Color(0xFFE91E63), 800: Color(0xFFD81B60), 900: Color(0xFFC2185B), }, );
これで(個人的には)いい感じのピンクになりました。
実際に製品として動くものを見たい
この機能は自分が作った以下アプリに実装されています。(プロフィール→設定→テーマ)
Wishes 〜みんなの夢リスト〜
play.google.com実際に触ってみたい方は是非〜!