「Flutter Advent Calendar 2022」に参加させて頂きます!21日目です。
Riverpod の頂いた表示と更新の質問
UdemyにてRiverpodを使ったMVVMパターンの講座を公開しています。
そのなかに「Riverpodで表示と更新の方法がいくつか紹介されているけど、どれを使うのがいいんですか」というものがありました。質問を頂いたときは、「おそらく」という回答しかできませんでしたが、Riverpodのバージョンが2になってから、明確に返答ができるようなりました。
そのため自分なりの回答と理由を述べます。
回答
Riverpodを使った値の表示
ref.watch(_stateProvider)
Riverpodを使った値の更新
新しい値で更新するとき
ref.read(_stateProvider.notifier).state = 1
テスト用の全ソースの中では、プログラムの仕様上、前回の値を使ってます。
実際はロジックの計算結果やユーザからの入力値が設定されます。
現在の値を使って更新するとき
ref.read(_stateProvider.notifier).update((state) => state + 1);
前の値を知って、その値に処理をして、値を更新するときに使います。
候補
以前の候補と駄目な理由を挙げていきます
Riverpodを使った値の駄目な表示
*ref.watch(_stateProvider.state).state
_stateProvider.stateのstateが非推奨になっている
*ref.watch(_stateProvider.notifier).state
リアクティブに表示されない
Riverpodを使った値の駄目な更新
- ref.read(_stateProvider.notifier).state = ref.read(_stateProvider.notifier).state + 1
更新はできるが、ref.read(_stateProvider.notifier).stateが長い - ref.read(_stateProvider4.state).state = ref.read(_stateProvider4.state).state + 1;
_stateProvider.stateのstateが非推奨になっている
まとめ
ということで、Riverpodの表示と更新方法を紹介しました。次に「どれを使えばいいんですか」と聞かれても、このページを回答すれば良くなりました。
テスト用の全ソース
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return ProviderScope(
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _stateProvider1 = StateProvider<int>((ref) => 0);
final _stateProvider2 = StateProvider<int>((ref) => 0);
final _stateProvider3 = StateProvider<int>((ref) => 0);
final _stateProvider4 = StateProvider<int>((ref) => 0);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Consumer(
builder: (context, ref, child) => Text(
'${ref.watch(_stateProvider1)}',
),
),
Consumer(
builder: (context, ref, child) => Text(
// 非推奨の表示
'${ref.watch(_stateProvider2.state).state}',
),
),
Consumer(
builder: (context, ref, child) => Text(
// リアクティブに更新されない
'${ref.watch(_stateProvider3.notifier).state}',
),
),
Consumer(
builder: (context, ref, child) => Text(
'${ref.watch(_stateProvider4)}',
),
),
],
),
),
floatingActionButton: Consumer(
builder: (context, ref, child) => FloatingActionButton(
onPressed: () {
// 両方OK (多分推奨)
ref.read(_stateProvider1.notifier).state =
ref.read(_stateProvider1) + 1;
ref.read(_stateProvider2.notifier).update((state) => state + 1);
// 両方OK
ref.read(_stateProvider3.notifier).state =
ref.read(_stateProvider3.notifier).state + 1;
// 非推奨
ref.read(_stateProvider4.state).state =
ref.read(_stateProvider4.state).state + 1;
ref.read(_stateProvider4.state).state =
ref.read(_stateProvider4.state).state + 1;
},
tooltip: 'Increment',
child: const Icon(Icons.add),
),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}