Skip to content

Instantly share code, notes, and snippets.

@temoki
Last active December 13, 2024 02:07
Show Gist options
  • Save temoki/0e9acedbfa2b6ebe424f7e856dc2f5f3 to your computer and use it in GitHub Desktop.
Save temoki/0e9acedbfa2b6ebe424f7e856dc2f5f3 to your computer and use it in GitHub Desktop.
//================================================================================
// 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 ใฎๅ…ฌๅผใƒ‰ใ‚ญใƒฅใƒกใƒณใƒˆใซใŠใ„ใฆใ‚‚็Šถๆ…‹็ฎก็†ๆ‰‹ๆณ•ใฎ๏ผ‘ใคใจใ—ใฆ็ดนไป‹ใ•ใ‚Œใฆใ„ใ‚‹
// * 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 ใฎๅ…ฌๅผใƒ‰ใ‚ญใƒฅใƒกใƒณใƒˆใซใŠใ„ใฆใ‚‚็Šถๆ…‹็ฎก็†ๆ‰‹ๆณ•ใฎ๏ผ‘ใคใจใ—ใฆ็ดนไป‹ใ•ใ‚Œใฆใ„ใ‚‹
// * 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