間違っている箇所、謎な箇所があったら指摘してください。
動機
riverpodの勉強がしたかった(建前)
自滅するWidgetが欲しかった(本音)
前提知識
riverpodをほんの少しだけ知っている。
バージョン
バージョン | |
---|---|
Flutter | 3.3.0 |
Dart | 2.18.0 |
flutter_riverpod | ^2.2.0 |
Xcode | 14.2 |
概要
ElevatedButton
はStateNotifier
のサブクラス越しに挿入する。
StateNotifierProvider
を使って、ボタンをタップした時にWidgetをnullにする。
今回はiOSシミュレーターを使ってる。
重要な部分だけ軽く抑えて、最後に全体のコードを貼る。
実装
provider辺りの部分
final buttonTogglerProvider =
StateNotifierProvider<ButtonToggler, VanishButton?>(
(ref) => ButtonToggler());
class ButtonToggler extends StateNotifier<VanishButton?> {
ButtonToggler() : super(const VanishButton());
void toggle() => state = state == null ? const VanishButton() : null;
}
Widgetとnullで切り替えたかったので、型はVanishButton?
を指定している。
ButtonToggler
内にtoggle()
を定義することで、自発的にWidgetを削除し、任意のタイミングで再構築できる。(←まさにこれがしたかった!!)
StateNotifierProvider
にしたのは、Widgetとnullの切り替えを自分で作りたかったから。他のでもいいかもしれないけど、公式パッと見た感じこれがベストっぽかった。
プロバイダとはでproviderの種類が確認できる。
state
のとこにButtonTogger
の状態が格納されてるイメージ。基本的にこのstateを参照していくっぽい。stateはもともとあるものなので自分で宣言したりしなくていい、てかしない。
ElevatedButtonの挙動のとこ
class VanishButton extends ConsumerWidget {
const VanishButton({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
return ElevatedButton(
onPressed: () {
ref.read(buttonTogglerProvider.notifier).toggle();
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.amber,
),
child: Text(
ref.watch(buttonTogglerProvider).toString(),
),
);
}
}
ref
で監視とかできるようにConsumerWidget
を継承する。
前項のButtonToggler
でVanishButton
を渡してるのは、このボタンを消したいから。そのため、このボタンのonPress
内でtoggle()を呼んで、間接的に自分をnullにして消す。(逆に渡したいのでクラスを定義するみたいなとこある)
onPress
の様な、ユーザーのアクションに起因する処理の中にはwatch()
ではなくread()
を使うらしい。read()にはbuttonTogglerProvider.notifier
を渡すところがwatch()と異なる。
watchは監視をするだけ?っぽい。
watch メソッドは ElevatedButton の onPressed 内など、非同期的な場面で呼び出さないでください。 また initState を始め、State のライフサイクルメソッド内での使用も避けてください。
これらの場合は代わりに ref.read を使用してください。
ref.read はリアクティブではないため、可能な限り使用を避けてください。
watch や listen の使用では問題が生じる場合の回避策として存在しています。 ほとんどの場面では watch や listen の使用、特に watch の使用がベターなはずです。
【重要】 ref.read は build メソッドの中で使わない
らしい。1
ref.watch()
は今回対して関係ないが、これで状態を監視できる。
スコープ
void main() {
runApp(const ProviderScope(child: MyApp()));
}
main()
でちゃんとスコープを決めとるのを忘れないのが大事。
実行結果 (GIF)
vanishButton消えちゃうので復活用のボタンも用意してた。
状態の変化がわかりやすいように一応テキストも変えた。
何もない時に復活ボタンを押しても何も起きない。
全体コード
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final buttonTogglerProvider =
StateNotifierProvider<ButtonToggler, VanishButton?>(
(ref) => ButtonToggler());
class ButtonToggler extends StateNotifier<VanishButton?> {
ButtonToggler() : super(const VanishButton());
void toggle() => state = state == null ? const VanishButton() : null;
}
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const CountPage(),
);
}
}
class CountPage extends ConsumerWidget {
const CountPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(
title: const Text("title"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(height: 50),
Container(
height: 50,
child: ref.watch(buttonTogglerProvider),
),
const SizedBox(height: 50),
Container(
height: 50,
child: ElevatedButton(
onPressed: () {
ref.watch(buttonTogglerProvider) ??
ref.read(buttonTogglerProvider.notifier).toggle();
},
child: Text(
ref.watch(buttonTogglerProvider) == null
? "restore button"
: "do noting",
),
),
),
],
),
),
);
}
}
class VanishButton extends ConsumerWidget {
const VanishButton({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
return ElevatedButton(
onPressed: () {
ref.read(buttonTogglerProvider.notifier).toggle();
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.amber,
),
child: Text(
ref.watch(buttonTogglerProvider).toString(),
),
);
}
}
参考
- https://riverpod.dev/ja/docs/concepts/reading
- https://riverpod.dev/ja/docs/concepts/providers
- https://www.flutter-study.dev/firebase-app/riverpod