Last active
December 13, 2024 02:07
-
-
Save temoki/0e9acedbfa2b6ebe424f7e856dc2f5f3 to your computer and use it in GitHub Desktop.
Open with DartPad 👉 https://dartpad.dev/?id=0e9acedbfa2b6ebe424f7e856dc2f5f3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //================================================================================ | |
| // Flutter State Management Guide (1) | |
| // | |
| // f(State) = UI | |
| // | |
| // It means the UI is a function of the current state. | |
| // When the state changes, the UI automatically updates to reflect it. | |
| //================================================================================ | |
| import 'package:flutter/material.dart'; | |
| import 'package:provider/provider.dart' as provider; | |
| import 'package:hooks_riverpod/hooks_riverpod.dart' as riverpod; | |
| void main() => runApp(const App()); | |
| final class App extends StatelessWidget { | |
| const App({super.key}); | |
| @override | |
| Widget build(BuildContext context) { | |
| return riverpod.ProviderScope( | |
| child: MediaQuery( | |
| data: MediaQuery.of(context).copyWith( | |
| textScaler: TextScaler.linear(2.0), | |
| ), | |
| child: MaterialApp( | |
| title: 'Flutter State Management Guide', | |
| debugShowCheckedModeBanner: false, | |
| theme: ThemeData(colorSchemeSeed: Colors.blue), | |
| home: Scaffold( | |
| appBar: AppBar(title: Text('Flutter State Management')), | |
| body: ListView( | |
| children: [ | |
| _ListItem( | |
| 1, | |
| 'Stateless', | |
| (_) => Case1Page(), | |
| ), | |
| _ListItem( | |
| 2, | |
| 'Stateful / Single Widget', | |
| (_) => Case2Page(), | |
| ), | |
| _ListItem( | |
| 3, | |
| 'Stateful / Multiple Widgets / State Lift Up', | |
| (_) => Case3Page(), | |
| ), | |
| _ListItem( | |
| 4, | |
| 'Stateful / Multiple Widgets / ChangeNotifier', | |
| (_) => Case4Page(), | |
| ), | |
| _ListItem( | |
| 5, | |
| 'Stateful / Multiple Widgets / ChangeNotifier + InheritedWidget', | |
| (_) => Case5Page(), | |
| ), | |
| _ListItem( | |
| 6, | |
| 'Stateful / Multiple Widgets / ChangeNotifier + Provider', | |
| (_) => Case6Page(), | |
| ), | |
| _ListItem( | |
| 7, | |
| 'Stateful / Multiple Widgets / ChangeNotifier + Riverpod', | |
| (_) => Case7Page(), | |
| ), | |
| _ListItem( | |
| 8, | |
| 'Stateful / Multiple Widgets / Notifier + Riverpod', | |
| (_) => Case8Page(), | |
| ), | |
| ], | |
| ), | |
| ), | |
| ), | |
| ), | |
| ); | |
| } | |
| } | |
| final class _ListItem extends StatelessWidget { | |
| _ListItem(this.no, this.description, this.builder); | |
| final int no; | |
| final String description; | |
| final WidgetBuilder builder; | |
| @override | |
| Widget build(BuildContext context) { | |
| final title = 'Case $no'; | |
| return ListTile( | |
| title: Text(title), | |
| subtitle: Text(description), | |
| onTap: () => Navigator.of(context).push( | |
| MaterialPageRoute( | |
| builder: (c) => Scaffold( | |
| appBar: AppBar( | |
| title: Text(title), | |
| bottom: _Description(description), | |
| ), | |
| body: builder(c), | |
| ), | |
| ), | |
| ), | |
| ); | |
| } | |
| } | |
| final class _Description extends StatelessWidget | |
| implements PreferredSizeWidget { | |
| _Description(this.description); | |
| final String description; | |
| @override | |
| Widget build(BuildContext context) { | |
| return SizedBox( | |
| height: 40, | |
| child: Text(description, style: TextStyle(fontSize: 10)), | |
| ); | |
| } | |
| @override | |
| Size get preferredSize => Size.fromHeight(40); | |
| } | |
| //================================================================================ | |
| // CASE 1 : Stateless | |
| // | |
| // * `StatelessWidget` は状態をもたない Widget | |
| // | |
| // ┌-----------------┐ | |
| // │ │ | |
| // │ StatelessWidget │ | |
| // │ │ | |
| // └-----------------┘ | |
| //================================================================================ | |
| final class Case1Page extends StatelessWidget { | |
| const Case1Page({super.key}); | |
| @override | |
| Widget build(BuildContext context) { | |
| debugPrint('Case1Page build'); | |
| return Center( | |
| child: Text("I'm static"), | |
| ); | |
| } | |
| } | |
| //================================================================================ | |
| // CASE 2 : Stateful / Single Widget | |
| // | |
| // * `StatefulWidget` は状態をもつ Widget | |
| // * `State` クラスのフィールドが状態となる | |
| // * `initState()` メソッドをオーバーライドすることで状態の初期化ができる(初回ビルド時のみ実行される) | |
| // * `setState()` メソッドの中でフィールドを更新することでリビルドを促せる | |
| // * この Widget 内のみで参照される状態なので local state や ephemeral state と呼ばれる | |
| // | |
| // ┌-----------------┐ | |
| // │ │ | |
| // │ StatefulWidget │ ┌-------┐ | |
| // │ ┼--------┼ State │ | |
| // └-----------------┘ └-------┘ | |
| //================================================================================ | |
| final class Case2Page extends StatefulWidget { | |
| const Case2Page({super.key}); | |
| State<Case2Page> createState() => Case2State(); | |
| } | |
| final class Case2State extends State<Case2Page> { | |
| Case2State(); | |
| int _count = -1; | |
| @override | |
| void initState() { | |
| _count = 0; | |
| super.initState(); | |
| } | |
| @override | |
| Widget build(BuildContext context) { | |
| debugPrint('Case2Page build : $_count'); | |
| return Center( | |
| child: Column( | |
| mainAxisSize: MainAxisSize.min, | |
| children: [ | |
| Text("Count = $_count"), | |
| const SizedBox(height: 8), | |
| FilledButton( | |
| onPressed: () { | |
| setState(() => _count++); | |
| }, | |
| child: const Text('+'), | |
| ), | |
| ], | |
| ), | |
| ); | |
| } | |
| } | |
| //================================================================================ | |
| // CASE 3 : Stateful / Multiple Widgets / State Lift Up | |
| // | |
| // * 複数の Widget で共通の状態がある場合、それらの共通の祖先の Widget まで状態を移動させる | |
| // * 祖先の Widget から、状態を必要とする子孫の Widge まで状態を伝搬させる(バケツリレー) | |
| // | |
| // ┌---------------┐ | |
| // │StatelessWidget│ | |
| // │ (A) │ | |
| // └-------┬-------┘ | |
| // │ | |
| // ┌------------┴-------------┐ | |
| // ┌-------┴-------┐ ┌-------┴-------┐ | |
| // │StatefulWidget │ ┌-----┐ │StatelessWidget│ | |
| // │ (B) ┼-┤State│ │ (C) │ | |
| // └---------------┘ └-----┘ └---------------┘ | |
| // | |
| // ↓ State Lift Up! ↓ | |
| // | |
| // ┌---------------┐ | |
| // │StatefulWidget │ ┌-----┐ | |
| // │ (A) ├-┤State│ | |
| // └-------┬-------┘ └-----┘ | |
| // │ | |
| // ┌------------┴-------------┐ | |
| // ┌-------┴-------┐ ┌-------┴-------┐ | |
| // │StatelessWidget│ │StatelessWidget│ | |
| // │ (B) │ │ (C) │ | |
| // └---------------┘ └---------------┘ | |
| // | |
| // Widget (has state) | |
| // ├ Child Widget (use state) | |
| // └ Button (update state) | |
| //================================================================================ | |
| final class Case3Page extends StatefulWidget { | |
| const Case3Page({super.key}); | |
| State<Case3Page> createState() => Case3State(); | |
| } | |
| final class Case3State extends State<Case3Page> { | |
| Case3State(); | |
| int _count = 0; | |
| @override | |
| Widget build(BuildContext context) { | |
| debugPrint('Case3State build'); | |
| return Center( | |
| child: Column( | |
| mainAxisSize: MainAxisSize.min, | |
| children: [ | |
| CountWidget(count: _count), | |
| CountDoubleWidget(count: _count), | |
| EvenOddWidget(count: _count), | |
| const SizedBox(height: 8), | |
| FilledButton( | |
| onPressed: () => setState(() => _count++), | |
| child: const Text('+'), | |
| ), | |
| ], | |
| ), | |
| ); | |
| } | |
| } | |
| final class CountWidget extends StatelessWidget { | |
| const CountWidget({super.key, required this.count}); | |
| final int count; | |
| @override | |
| Widget build(BuildContext context) { | |
| return Container( | |
| color: Colors.cyan, | |
| padding: EdgeInsets.all(8), | |
| child: Text('CountWidget\nCount = $count'), | |
| ); | |
| } | |
| } | |
| final class CountDoubleWidget extends StatelessWidget { | |
| const CountDoubleWidget({super.key, required this.count}); | |
| final int count; | |
| @override | |
| Widget build(BuildContext context) { | |
| return Container( | |
| color: Colors.lime, | |
| padding: EdgeInsets.all(8), | |
| child: Text('CountDoubleWidget\nCount x 2 = ${count * 2}'), | |
| ); | |
| } | |
| } | |
| final class EvenOddWidget extends StatelessWidget { | |
| const EvenOddWidget({super.key, required this.count}); | |
| final int count; | |
| @override | |
| Widget build(BuildContext context) { | |
| return Container( | |
| color: Colors.yellow, | |
| padding: EdgeInsets.all(8), | |
| child: Text('EvenOddWidget\nCount is ${count % 2 == 0 ? 'even' : 'odd'}'), | |
| ); | |
| } | |
| } | |
| //================================================================================ | |
| // CASE 4 : Stateful / Multiple Widgets / ChangeNotifier | |
| // | |
| // * 状態とその状態更新ロジックを同居させられるのが `ChangeNotifier` | |
| // * https://api.flutter.dev/flutter/foundation/ChangeNotifier-class.html | |
| // * `notifyListeners()` により状態の更新を通知することができる | |
| // * `ListenableBuilder()` に `ChangeNotifier` を指定すると、その配下の Widget は | |
| // `ChangeNotifier` の状態が更新されるたびにリビルドされる | |
| // | |
| // ┌-----------------┐ | |
| // │ │ ┌--------------┐ | |
| // │ StatelessWidget │ │ChangeNotifier│ | |
| // │ ┼--┼ (State) │ | |
| // └--------┬--------┘ └--------------┘ | |
| // │ | |
| // ┌--------┴--------┐ | |
| // │ListenableBuilder│ | |
| // └--------┬--------┘ | |
| // │ | |
| // ┌----------┴-----------┐ | |
| // │Widget that uses state│ | |
| // └----------------------┘ | |
| // | |
| // Widget (has ChangeNotifier) | |
| // └ ListenableBuilder (Rebuild on ChangeNotifier change) | |
| // ├ Child Widget (use state via ChangeNotifier) | |
| // └ Button (update state via ChangeNotifier) | |
| //================================================================================ | |
| final class CounterChangeNotifier with ChangeNotifier { | |
| int _count = 0; | |
| int get count => _count; | |
| void increment() { | |
| _count++; | |
| notifyListeners(); | |
| } | |
| } | |
| final class Case4Page extends StatelessWidget { | |
| Case4Page(); | |
| final _counter = CounterChangeNotifier(); | |
| @override | |
| Widget build(BuildContext context) { | |
| debugPrint('Case4Page build'); | |
| return ListenableBuilder( | |
| listenable: _counter, | |
| builder: (_, __) { | |
| debugPrint('Case4Page/ListenableBuilder child build'); | |
| return Center( | |
| child: Column( | |
| mainAxisSize: MainAxisSize.min, | |
| children: [ | |
| CountWidget(count: _counter.count), | |
| CountDoubleWidget(count: _counter.count), | |
| EvenOddWidget(count: _counter.count), | |
| const SizedBox(height: 8), | |
| FilledButton( | |
| onPressed: () => _counter.increment(), | |
| child: const Text('+'), | |
| ), | |
| ], | |
| ), | |
| ); | |
| }, | |
| ); | |
| } | |
| } | |
| //================================================================================ | |
| // CASE 5 : Stateful / Multiple Widgets / ChangeNotifier + InheritedWidget | |
| // | |
| // * `InheritedWidget` の子孫の Widget は `{InheritedWidget}.of(context)` にて | |
| // 祖先にいる最初の `InheritedWidget` を取得することができる | |
| // * https://api.flutter.dev/flutter/widgets/InheritedWidget-class.html | |
| // * `InheritedWidget` の実装が面倒、わかりにくい | |
| // | |
| // ┌-----------------┐ | |
| // │ │ ┌--------------┐ | |
| // │ InheritedWidget │ │ChangeNotifier│ | |
| // ┌---►│ ┼--┼ (State) │ | |
| // │ └--------┬--------┘ └--------------┘ | |
| // │ │ | |
| // │ ┌--------┴--------┐ | |
| // │ │ (Child) │ | |
| // │ └--------┬--------┘ | |
| // │ │ | |
| // │ ... | |
| // │ │ | |
| // │of ┌--------┴--------┐ | |
| // └----┼ (Descendant) │ | |
| // └--------┬--------┘ | |
| // │ | |
| // ┌--------┴--------┐ | |
| // │ListenableBuilder│ | |
| // └--------┬--------┘ | |
| // │ | |
| // ┌----------┴-----------┐ | |
| // │Widget that uses state│ | |
| // └----------------------┘ | |
| // | |
| // InheritedWidget (has ChangeNotifier) | |
| // └ Child Widget | |
| // └ ... | |
| // └ ListenableBuilder (Rebuild on ChangeNotifier change) | |
| // ├ Descendant Widget 1 (use state via ChangeNotifier) | |
| // └ Button (update state via ChangeNotifier) | |
| //================================================================================ | |
| class CounterInheritedWidget extends InheritedWidget { | |
| const CounterInheritedWidget({ | |
| super.key, | |
| required this.counter, | |
| required super.child, | |
| }); | |
| final CounterChangeNotifier counter; | |
| @override | |
| bool updateShouldNotify(CounterInheritedWidget oldWidget) => false; | |
| static CounterInheritedWidget of(BuildContext context) { | |
| final widget = context | |
| .getElementForInheritedWidgetOfExactType<CounterInheritedWidget>() | |
| ?.widget as CounterInheritedWidget?; | |
| if (widget != null) { | |
| return widget; | |
| } | |
| throw Exception('No Case3CounterInheritedWidget found in context'); | |
| } | |
| } | |
| final class Case5Page extends StatelessWidget { | |
| Case5Page(); | |
| @override | |
| Widget build(BuildContext context) { | |
| debugPrint('Case5Page build'); | |
| return CounterInheritedWidget( | |
| counter: CounterChangeNotifier(), | |
| child: Case5ChildWidget(), | |
| ); | |
| } | |
| } | |
| final class Case5ChildWidget extends StatelessWidget { | |
| @override | |
| Widget build(BuildContext context) { | |
| debugPrint('Case5ChildWidget build'); | |
| final counter = CounterInheritedWidget.of(context).counter; | |
| return ListenableBuilder( | |
| listenable: counter, | |
| builder: (_, __) { | |
| debugPrint('Case5ChildWidget/ListenableBuilder child build'); | |
| return Center( | |
| child: Column( | |
| mainAxisSize: MainAxisSize.min, | |
| children: [ | |
| CountWidget(count: counter.count), | |
| CountDoubleWidget(count: counter.count), | |
| EvenOddWidget(count: counter.count), | |
| const SizedBox(height: 8), | |
| FilledButton( | |
| onPressed: () => counter.increment(), | |
| child: const Text('+'), | |
| ), | |
| ], | |
| ), | |
| ); | |
| }, | |
| ); | |
| } | |
| } | |
| //================================================================================ | |
| // CASE 6 : Stateful / Multiple Widgets / ChangeNotifier + Provider | |
| // | |
| // * `InheritedWidget` と同等のことを簡単にしてくれるのが 3rd Party の Provider パッケージ | |
| // * https://pub.dev/packages/provider | |
| // * Flutter の公式ドキュメントにおいても状態管理手法の1つとして紹介されている | |
| // * https://docs.flutter.dev/data-and-backend/state-mgmt/simple | |
| // | |
| // ┌-----------------┐ ┌--------------┐ | |
| // │ ChangeNotifier │ │ChangeNotifier│ | |
| // ┌---►│ Provider ┼--┼ (State) │ | |
| // │ └--------┬--------┘ └--------------┘ | |
| // │ │ | |
| // │ ┌--------┴--------┐ | |
| // │ │ (Child) │ | |
| // │ └--------┬--------┘ | |
| // │ │ | |
| // │ ... | |
| // │ │ | |
| // │of ┌--------┴--------┐ | |
| // └----┼ (Descendant) │ | |
| // └--------┬--------┘ | |
| // │ | |
| // ┌----------┴-----------┐ | |
| // │Widget that uses state│ | |
| // └----------------------┘ | |
| // | |
| // Widget | |
| // └ ChangeNotifierProvider (has ChangeNotifier) | |
| // └ ... | |
| // └ Consumer<ChangeNotifier> (Rebuild on ChangeNotifier change) | |
| // ├ Descendant Widget 1 (use state via ChangeNotifier) | |
| // └ Button (update state via ChangeNotifier) | |
| //================================================================================ | |
| final class Case6Page extends StatelessWidget { | |
| Case6Page(); | |
| @override | |
| Widget build(BuildContext context) { | |
| debugPrint('Case6Page build'); | |
| return provider.ChangeNotifierProvider( | |
| create: (context) => CounterChangeNotifier(), | |
| child: Case6ChildWidget(), | |
| ); | |
| } | |
| } | |
| final class Case6ChildWidget extends StatelessWidget { | |
| @override | |
| Widget build(BuildContext context) { | |
| debugPrint('Case6ChildWidget build'); | |
| return provider.Consumer<CounterChangeNotifier>( | |
| builder: (_, counter, __) { | |
| debugPrint('Case5ChildWidget/Consumer child build'); | |
| return Center( | |
| child: Column( | |
| mainAxisSize: MainAxisSize.min, | |
| children: [ | |
| CountWidget(count: counter.count), | |
| CountDoubleWidget(count: counter.count), | |
| EvenOddWidget(count: counter.count), | |
| const SizedBox(height: 8), | |
| FilledButton( | |
| onPressed: () => counter.increment(), | |
| child: const Text('+'), | |
| ), | |
| ], | |
| ), | |
| ); | |
| }, | |
| ); | |
| } | |
| } | |
| //================================================================================ | |
| // CASE 7 : Stateful / Multiple Widgets / ChangeNotifier + Riverpod | |
| // | |
| // * Provider パッケージの作者(Remiさん)が、Provider の問題点を解消すべく開発したのが Riverpod | |
| // * https://riverpod.dev | |
| // * 名称は Provider のアナグラム | |
| // * Provider パッケージでは同じクラスのオブジェクトを複数提供できない | |
| // * こちらも Flutter の公式ドキュメントにおいても状態管理手法の1つとして紹介されている | |
| // * https://docs.flutter.dev/data-and-backend/state-mgmt/simple | |
| // * Riverpod の Provider(同じ名前でややこしい)は、提供したいオブジェクトの生成方法と | |
| // そのオブジェクトにアクセスするためのキーを担う | |
| // * Riverpod の Provider にアクセスできるのは `ConsumerWidget` を継承した Widget のみで、 | |
| // ビルドメソッドに追加された `WidgetRef` を介してアクセスする | |
| // * `WidgetRef` の `watch` を使えば、そのオブジェクトの状態が変わった時にリビルドが走る | |
| // | |
| // ProviderScope (has ChangeNotifier) | |
| // └ ... | |
| // └ ConsumerWidget (watch ChangeNotifier via Provider, Rebuild on ChangeNotifier change) | |
| // ├ Descendant Widget 1 (use state via ChangeNotifier) | |
| // └ Button (update state via ChangeNotifier) | |
| //================================================================================ | |
| final counterChangeNotifierProvider = | |
| riverpod.ChangeNotifierProvider<CounterChangeNotifier>((ref) { | |
| return CounterChangeNotifier(); | |
| }); | |
| final class Case7Page extends riverpod.ConsumerWidget { | |
| @override | |
| Widget build(BuildContext context, riverpod.WidgetRef ref) { | |
| debugPrint('Case7Page build'); | |
| final counter = ref.watch(counterChangeNotifierProvider); | |
| return Center( | |
| child: Column( | |
| mainAxisSize: MainAxisSize.min, | |
| children: [ | |
| CountWidget(count: counter.count), | |
| CountDoubleWidget(count: counter.count), | |
| EvenOddWidget(count: counter.count), | |
| const SizedBox(height: 8), | |
| FilledButton( | |
| onPressed: () => counter.increment(), | |
| child: const Text('+'), | |
| ), | |
| ], | |
| ), | |
| ); | |
| } | |
| } | |
| //================================================================================ | |
| // CASE 8 : Stateful / Multiple Widgets / Notifier + Riverpod | |
| // | |
| // * Riverpod が `ChangeNotifier` の代替として提供するのが `Notifier` | |
| // * `Notifier` がもつ `ref` により他の Provider が提供するオブジェクトにアクセスできる | |
| // * `Notifier` の Provider となるのが `NotifierProvider` で、こちらも `ConsumerWidget` | |
| // を継承した Widget から `WidgetRef` を介してアクセスする | |
| // * `ref.watch(someNotifierProvider)` で `Notifier` のもつ状態変化によりリビルドできる | |
| // * Provider の `select` で、状態のフィルタリングができる | |
| // * `ref.read(someNotifierProvider.notifier)` で `Notifier` そのものにアクセスできる | |
| // * `ref.listen(someNotifierProvider, ...)` で状態変化を検知できる | |
| // * `ref.invalidate(someNotifierProvider)` で `Notifier` のリビルドができる | |
| // | |
| // ProviderScope (has ChangeNotifier) | |
| // └ ... | |
| // └ ConsumerWidget (watch ChangeNotifier via Provider, Rebuild on ChangeNotifier change) | |
| // ├ Descendant Widget 1 (use state via ChangeNotifier) | |
| // └ Button (update state via ChangeNotifier) | |
| //================================================================================ | |
| final class CounterNotifier extends riverpod.Notifier<int> { | |
| @override | |
| int build() { | |
| // You can watch the other provider | |
| // final hoge = ref.watch(otherStateProvider); | |
| return 0; | |
| } | |
| void increment() => state++; | |
| } | |
| final counterNotifierProvider = | |
| riverpod.NotifierProvider<CounterNotifier, int>(() { | |
| return CounterNotifier(); | |
| }); | |
| final class Case8Page extends riverpod.ConsumerWidget { | |
| const Case8Page({super.key}); | |
| @override | |
| Widget build(BuildContext context, riverpod.WidgetRef ref) { | |
| debugPrint('Case8Page build'); | |
| final counter = ref.read(counterNotifierProvider.notifier); | |
| ref.listen(counterNotifierProvider.select((count) => count ~/ 10), | |
| (_, next) { | |
| ScaffoldMessenger.of(context).showSnackBar( | |
| SnackBar(content: Text('Count is a multiple of 10 : $next')), | |
| ); | |
| }); | |
| return Center( | |
| child: Column( | |
| mainAxisSize: MainAxisSize.min, | |
| children: [ | |
| CountConsumerWidget(), | |
| CountDoubleConsumerWidget(), | |
| EvenOddConsumerWidget(), | |
| const SizedBox(height: 8), | |
| FilledButton( | |
| onPressed: () => counter.increment(), | |
| child: const Text('+'), | |
| ), | |
| const SizedBox(height: 4), | |
| FilledButton( | |
| onPressed: () => ref.invalidate(counterNotifierProvider), | |
| child: const Text('Reset'), | |
| ), | |
| ], | |
| ), | |
| ); | |
| } | |
| } | |
| final class CountConsumerWidget extends riverpod.ConsumerWidget { | |
| const CountConsumerWidget({super.key}); | |
| @override | |
| Widget build(BuildContext context, riverpod.WidgetRef ref) { | |
| final count = ref.watch(counterNotifierProvider); | |
| return Container( | |
| color: Colors.cyan, | |
| padding: EdgeInsets.all(8), | |
| child: Text('CountConsumerWidget\nCount = $count'), | |
| ); | |
| } | |
| } | |
| final class CountDoubleConsumerWidget extends riverpod.ConsumerWidget { | |
| const CountDoubleConsumerWidget({super.key}); | |
| @override | |
| Widget build(BuildContext context, riverpod.WidgetRef ref) { | |
| final countDouble = | |
| ref.watch(counterNotifierProvider.select((count) => count * 2)); | |
| return Container( | |
| color: Colors.lime, | |
| padding: EdgeInsets.all(8), | |
| child: Text('CountDoubleConsumerWidget\nCount × 2 = $countDouble'), | |
| ); | |
| } | |
| } | |
| final class EvenOddConsumerWidget extends riverpod.ConsumerWidget { | |
| const EvenOddConsumerWidget({super.key}); | |
| @override | |
| Widget build(BuildContext context, riverpod.WidgetRef ref) { | |
| final isEven = | |
| ref.watch(counterNotifierProvider.select((count) => count % 2 == 0)); | |
| return Container( | |
| color: Colors.yellow, | |
| padding: EdgeInsets.all(8), | |
| child: Text('EvenOddConsumerWidget\nCount is ${isEven ? 'even' : 'odd'}'), | |
| ); | |
| } | |
| } | |
| //================================================================================ | |
| // Others : https://github.com/temoki/flutter_state_management | |
| //================================================================================ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment