Open7
「Riverpod」を勉強するスレ
データの受け渡しの場合、
- データを渡す側
- データを受け取る側
が必要。
Riverpodでは、データを渡す側
を 「Provider」(グローバル変数)で行う。
Provider (データを渡す側)の種類
- Provider
- 任意のデータを渡す / 他Providerに依存したProviderを作成する時に使う
- FutureProvider
-
Future
から取得できる任意のデータを渡す
-
- StreamProvider
-
Stream
から取得できる任意のデータを渡す
-
- StateProvide
- 更新可能な任意のデータを渡す
- StateNotifierProvider
-
StateNotifier
から取得できる任意のデータを渡す
-
- ScopedProvider
- 場所に応じて、異なる任意のデータを渡す
データを受け取る側
- ConsumerWidget
- 継承することで、データを受け取れるWidget
- Consumer
- コールバック内でデータを受け取れるWidget
- useProvider()
-
flutter_hooks
を使い、HookWidgetを継承したWidgetでデータを受け取れる関数
-
- context.read()
-
BuildContext
からデータを受け取る関数(データの更新通知は不可)
-
- ProviderContainer
- Flutterに依存せず、データを受け取れる
StateNotifierとは?
ドキュメントを読む限りだとクラスに1つだけ変数を作り、状態を管理する方法なのかな?
- 以前の状態と新しい状態を比較する
- 元に戻る-やり直しメカニズムを実装する
- アプリケーションの状態をデバッグする
詳しく理解していないから、ちゃんと調べて理解する。
プログラムサンプル
class Counter extends StateNotifier<int> {
Counter(): super(0); // 初期値をコンストラクタに渡す
void increment() => state++;
void decrement() => state--;
}
ざっくり概要がわかる記事
Riverpodとは?
flutter_hooksとは?
イミュータブルのメリット
公式チュートリアル
Provider
Providerは状態の一部をカプセル化し、その状態をリッスンできるようにしたオブジェクトである。
なぜ使用するのか?
状態の一部をラップすることにより、
- 複数の場所でアクセス可能
- Singletons, Service Locators, Dependency Injection or InheritedWidgetの上位互換
- 状態を組み合わせることが容易
- プロパイダー内に単純な構文で直接構築可能
- パフォーマンスの最適化
- 状態の変化に影響される部分だけ、再計算されることを保証する
- テストがしやすい
- 複雑な
setUp
/tearDown
は必要ない - どのProviderもオーライドし、テスト中に異なる動作が可能
- 複雑な
Providerを作成する
最も一般的な使用方法は、グローバル定数として宣言する
スタンダードな使用法
final myProvider = Provider((ref) {
return MyValue();
});
これは3つのコンポーネントで構成している
-
ffinal myProvider
変数- Providerの状態を読み取るために使用し、常に不変である必要がある。
-
Provider
- 変更できないオブジェクトを提供する
- 共有状態を作成する関数
- 関数は常に
ref
パラメータとして呼び出し、オブジェクトを受け取る - 状態が吐きされたとき、他Providerの読み取りや操作を実行可能
StateProvider
/// Providers are declared globally and specifies how to create a state
final counterProvider = StateProvider((ref) => 0);
read()
print(ref.read(counterProvider).runtimeType); // int
print(ref.read(counterProvider)); // 18
print(ref.read(counterProvider.notifier).runtimeType); // StateController<int>
print(ref.read(counterProvider.notifier)); // Instance of 'StateController<int>'
print(ref.read(counterProvider.state).runtimeType); // StateController<int>
print(ref.read(counterProvider.state)); // Instance of 'StateController<int>'
print(ref.read(counterProvider.state).state.runtimeType); // int
print(ref.read(counterProvider.state).state); // 18
print(ref.read(counterProvider.notifier).state.runtimeType); // int
print(ref.read(counterProvider.notifier).state); // 18
.read
ref.read
メソッドは、プロバイダの状態を余分な影響を与えずに取得する方法。
状態をリッスンしないため、値が更新されても検知できない。
ref.read
の使用はできるだけ避ける。これは、watch
やlisten
が使いにくい時の回避策として存在する。そのため、基本的にはwatch
もしくはlisten
を使う。ベストはwatch
だよ!!!
引用元 : Using ref.read to obtain the state of a provider once Note
Counterをコードリーディング
ConsumerWidget
Home
のUIが全て再描画される。
⚠️build
内で状態の変更やhttpリクエストをしない
Consumer
Consumer
でwrapされた箇所が再描画される。
今回の最適解
ConsumerWidgetの最適解
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
// A Counter example implemented with riverpod
void main() {
runApp(
// Adding ProviderScope enables Riverpod for the entire project
const ProviderScope(child: MyApp()),
);
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(home: Home());
}
}
/// Providers are declared globally and specifies how to create a state
final counterProvider = StateProvider((ref) => 0);
class Home extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider.state).state;
return Scaffold(
appBar: AppBar(title: const Text('Counter example')),
body: Center(
child: Text('$count'),
),
floatingActionButton: FloatingActionButton(
// The read method is an utility to read a provider without listening to it
onPressed: () => ref.watch(counterProvider.notifier).state++,
child: const Icon(Icons.add),
),
);
}
}
ConsumerWidget
でラップしているから、このサンプルだとConsumer
は必要ないと思う。