【Flutter】riverpodを使ってRadioListTileからAppBarのbackgroundColorを変える
背景
私はFlutterの勉強として簡単なメモアプリを作っています。現在はメモの追加や編集、削除、またアプリの説明画面やあらかじめ用意しておいたGoogle formでアンケートに答えてもらうフィードバック機能を実装しており、アプリ自体は大体完成となっています。
よし!あとはデプロイだな!
しかし、1つの願望が頭に浮かんできました...。
(AppBarの色をユーザー側から変更できるようにしたいな...)
このような「やってみたい」という動機から開発に至りました。
実装したいこと
- riverpodのStateProviderを組み合わせてColorを返すようにする
- 設定画面の大枠を作り、ドローワーから遷移できるようにする
- 設定画面に
ExpansionTile
を使ってドロップダウン実装する -
ExpansionTile
の中にRadioListTile
を並べる -
RadioListTile
でAppBarのbackgroundColor
を変更する処理を書く
riverpodのStateProviderを組み合わせてColorを返すようにする
準備するStateProviderは以下の2つです。
- 列挙型(enum)を監視するStateProvider
- それを監視して列挙型の値に応じてColorを返すStateProvider
今回、RadioListTile
を使用するので値は列挙型を使います。また、AppBar
のbackgroundColor
はColorクラスを使って指定するので1のStateProviderを監視して、その値によってColorを返すように条件分岐してあげましょう。
以下はコードの全体です。
import 'package:flutter_riverpod/flutter_riverpod.dart';
//列挙型の変数AppBarColor
enum AppBarColor { blue, red, green }
//AppBarColorを監視するStateProvider
final appBarColorProvider = StateProvider<Enum>((ref) => AppBarColor.blue);
//appBarColorProviderを監視しColorを返すStateProvider
final appBarColorStateProvider = StateProvider<Color>((ref) {
final appBarColorState = ref.watch(appBarColorProvider);
if (appBarColorState == AppBarColor.blue) {
return Colors.lightBlue;
} else if (appBarColorState == AppBarColor.red) {
return Colors.red;
} else {
return Colors.green;
}
});
解説
まずflutter_riverpodパッケージをimportしてriverpodを使えるようにしましょう。
import 'package:flutter_riverpod/flutter_riverpod.dart';
以下のリンクからriverpodのpub.devにアクセスできます。インストールの仕方等はそちらを参照してください。
次に、RadioListTile
で使用する列挙型の変数AppBarColorを宣言します。
今回はblue(正確にはlightblue)、red、greenの3種類にしました。
//列挙型の変数AppBarColor
enum AppBarColor { blue, red, green }
最後に2つのStateProviderを作ります。
列挙型のAppBarColorを監視するStateProviderのrefにはAppBarColor.blueを格納してあげます。
//AppBarColorを監視するStateProvider
final appBarColorProvider = StateProvider<Enum>((ref) => AppBarColor.blue);
さらにそれを監視し列挙型の値に応じたColorを返すStateProviderを作ります。
//appBarColorProviderを監視しColorを返すStateProvider
final appBarColorStateProvider = StateProvider<Color>((ref) {
final appBarColorState = ref.watch(appBarColorProvider);
if (appBarColorState == AppBarColor.blue) {
return Colors.lightBlue;
} else if (appBarColorState == AppBarColor.red) {
return Colors.red;
} else {
return Colors.green;
}
});
設定画面の大枠を作り、ドローワーから遷移できるようにする
設定画面の大枠を作ります。
設定画面のAppBar
のbackgroundColor
も変えたいのでConsumerStatefulWidgetで作っていきます。
import 'package:flutter_riverpod/flutter_riverpod.dart';
class SettingPage extends ConsumerStatefulWidget{
_SettingPageState createState() => _SettingPageState();
}
class _SettingPageState extends ConsumerState<SettingPage>{
void initState() {
super.initState();
ref.read(appBarColorStateProvider);
}
Widget build(BuildContext context){
return Scaffold(
appBar: AppBar(
centerTitle: true,
backgroundColor: ref.watch(appBarColorStateProvider),
title: const Text(
'設定',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: 30,
),
),
bottomOpacity: 0.0,
elevation: 0.0,
),
body: Column(children: <Widget>[
//ここにExpansionTileなどを書く
]),
);
}
}
ConsumerStatefulWidget
とConsumerState
というモノがでてきましたが、これはStatefulWidget
とState
に対応しています。
以下のリンクでその辺について詳しく書かれているので参照してみてください。
さて、設定画面の大枠ができました。ドローワーからこの設定画面へ遷移できるようにしましょう。
class CreateDrawer extends StatelessWidget{
Widget build(BuildContext context){
return Drawer(
child: Padding(
padding: const EdgeInsets.all(30.0),
child: ListView(
children: [
//アプリの説明画面、フィードバック画面に遷移するコード
OutlinedButton.icon(
onPressed: () {
//設定画面へ遷移するコード
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) {
return SettingPage();
},
),
);
},
icon: const Icon(
Icons.settings,
),
label: const Text(
'設定',
),
style: OutlinedButton.styleFrom(
side: const BorderSide(
color: Colors.grey,
width: 1,
),
),
),
],
),
),
);
}
}
このCreateDrawer
クラスをメイン画面にしたいStatelessWidget
のScaffold
内にあるdrawer
に置いてあげましょう。Container
でラップしてdrawer
の横幅をカスタムしてみると良いかもしれませんね。それらのコードは今回は省略します。
ExpansionTile
を使ってドロップダウン実装する
設定画面にドローワーから設定画面への遷移が実装できたところで設定画面にドロップダウンを実装しましょう。
DropdownButton
というドロップダウンメニューを実装できるWidgetがありますが、今回はラジオボタンを使いたいという考えがあるので、ExpansionTile
を採用しました。
class _SettingPageState extends ConsumerState<SettingPage>{
void initState() {
//省略
}
Widget build(BuildContext context){
return Scaffold(
appBar: AppBar(
//省略
),
body: Column(children: <Widget>[
ExpansionTile(
title: const Text('アプリバーのカラー'),
children: <Wisget>[
//ここにRadioListTileの値を変える処理を書く
],
),
]),
);
}
}
プロパティはシンプルにtitle
とchildren
のみにしました。
その他のExpansionTile
に関する情報は以下のリンクを参照してください。
RadioListTile
でAppBarのbackgroundColor
を変更する処理を書く
最後にRadioListTileを使ってAppBarのカラーを変更する処理を書いていきます。
class _SettingPageState extends ConsumerState<SettingPage>{
void initState() {
//省略
}
Widget build(BuildContext context){
return Scaffold(
appBar: AppBar(
//省略
),
body: Column(children: <Widget>[
ExpansionTile(
title: const Text('アプリバーのカラー'),
children: <Wisget>[
RadioListTile(
title: const Text('ライトブルー'),
value: AppBarColor.blue,
groupValue: ref.watch(appBarColorProvider),
onChanged: (Enum? value) {
ref.watch(appBarColorProvider.notifier).state = value!;
},
),
RadioListTile(
title: const Text('レッド'),
value: AppBarColor.red,
groupValue: ref.watch(appBarColorProvider),
onChanged: (Enum? value) {
ref.watch(appBarColorProvider.notifier).state = value!;
},
),
RadioListTile(
title: const Text('グリーン'),
value: AppBarColor.green,
groupValue: ref.watch(appBarColorProvider),
onChanged: (Enum? value) {
ref.watch(appBarColorProvider.notifier).state = value!;
},
),
],
),
]),
);
}
}
value
にはそれぞれtitle
に応じた値にして、groupValue
にはappBarColorProvider
をref.watch()
した値にしています。
onChanged
メソッドはvalue
プロパティが列挙型なので、それと一致するように列挙型の変数value
を引数に渡してあげて
ref.watch(appBarColorProvider.notifier).state = value!;
と書いてあげます。ぶっちゃけ.notifier
とか.state
の意味はよく分かっていません...。
デモンストレーション
AppBar
のbackgroundColor
をRadioListTile
で変更することができました。
最後に
ここまで読んでいただきありがとうございました!間違っている情報がある場合はお伝えください。
今後の展望としては、アプリを再度立ち上げるとせっかく設定したbackgroundColorが初期値のライトブルーに戻ってしまうので、shared_preferencesというパッケージを使って端末内に設定を保存できるようにすることです。
以下はそのパッケージのリンクです。
Qiitaでも記事を執筆していますので、そちらもよろしければご覧ください!
Discussion