この記事は昨日Riverpodを触った人がノート感覚でまとめました。
本当に吸収が早い学生だと思っているので、求人オファーお待ちしてます。
Provider
https://riverpod.dev/ja/docs/providers/provider/
Provider はプロバイダの中で最もベーシックなプロバイダであり、値を同期的に生成してくれます。
/// countProviderの値を2倍にする。
final doubleCountProvider = Provider<int>((ref) {
final count = ref.watch(countProvider);
return count * 2;
});
//値を読み込む
int doubleCount = ref.watch(doubleCountProvider);
この例では、countProvider
というプロバイダーを監視し、2倍の値を返すものです。
値を代入することはできません。つまり値を監視して、適当な処理をした値を返す用途として使えます。
他にも、キャッシュとして利用することもできます。
公式のキャッシュとしてProviderを利用する例
StateProvider
https://riverpod.dev/ja/docs/providers/state_provider
StateProvider は外部から変更が可能なステート(状態)を公開するプロバイダです。
final countStateProvider = StateProvider<int>((ref) => 0);
//値を読み込む
int countState = ref.watch(countStateProvider);
//書き込む時(パターン1)
ref.read(countStateProvider.notifier).state = 1;
//書き込む時(パターン2)
ref.read(countStateProvider.notifier).update((state) => state + 1);
この例では、int型のproviderを定義し、初期値は0となっています。
好きな型をRiverpodで扱えるというものです。後述するStateNotifierProvider
は関数を定義するなどして、細かいことができますが、こちらは反対で普通の変数を扱うような感覚で利用できます。
書き込みはupdate
を使用することで、現在の値を元に新しい値を決めることができます。
StateNotifierProvider
https://riverpod.dev/ja/docs/providers/state_notifier_provider
StateNotifierProvider は StateNotifier(Riverpod が依存する state_notifier パッケージのクラス)を監視し、公開するためのプロバイダです。
後述するStateNotifier
を扱うためのProviderです。前述のStateProvider
が変数を管理するものでしたが、こちらはクラスを管理することができます。StateNotifier
の説明をしてから詳しく説明します
StateNotifier
StateNotifierProvider
が扱えるProviderです。クラスとして定義するので、メゾットなどを用いて幅広い使い方ができます。
class CountStateNotifier extends StateNotifier<int> {
CountStateNotifier() : super(0);
void increment() {
state++;
}
}
この例では、int型のStateを作成し、初期値に0を指定しています。
そして、increment
を呼び出すことにより、値を1増加させます。
StateNotifierProvider(続き)
final countStateNotifierProvider =
StateNotifierProvider<CountStateNotifier, int>((ref) {
return CountStateNotifier();
});
//値を読み込む
int countStateNotifier = ref.watch(countStateNotifierProvider);
//メゾットを呼び出す
ref.read(countStateNotifierProvider.notifier).increment();
StateNotifier
を監視するためのProviderを作成しています。<>の左側には利用したいStateNotifier
を、右側には返したい型を指定します。
FutureProvider
https://riverpod.dev/ja/docs/providers/future_provider
FutureProvider は非同期操作が可能な Provider であると言えます
/// 5秒後に経過しました!が非同期で返される。
final countFutureProvider = FutureProvider<String>((ref) async {
await Future.delayed(const Duration(seconds: 5));
return "5秒経過しました!";
});
// 値を読み込む
AsyncValue<String> countFuture = ref.watch(countFutureProvider);
// Widgetとして利用してみる
countFuture.when(
loading: () => const Text("Loading"),
error: (_, __) => const Text("Error"),
data: (String data) {
return Text(data);
})
この例はは、lateFutureProviderが5秒後に5秒経過しました!
となるプログラムです。
非同期なものを使いたい時はこのProviderを使用することになるでしょう。
StreamProvider
https://riverpod.dev/ja/docs/providers/stream_provider
StreamProvider は FutureProvider の Stream 版です。
///1秒ごとにランダムな値を出すStream
Stream<int> randomValueStream() async*{
final random = Random();
while(true){
await Future.delayed(const Duration(seconds: 1));
yield random.nextInt(100);
}
}
//Providerを作成
final countStreamProvider = StreamProvider<int>((ref) => randomValueStream());
//値を読み込む
AsyncValue<int> countStream = ref.watch(countStreamProvider);
FutureProvider
のStreamバージョンです。
FirebaseやWebhookなど、リアルタイムで変化する値がある際に使用します。
ChangeNotifierProvider
https://riverpod.dev/ja/docs/providers/change_notifier_provider
ChangeNotifier を Flutter で利用するためのプロバイダです。
公式が非推奨としています。
// 使用するChangeNotifier
class CountChangeNotifier extends ChangeNotifier {
int count = 0;
void increment() {
count ++;
}
}
//ChangeNotifierProviderを作成
final countChangeNotifierProvider =
ChangeNotifierProvider<CountChangeNotifier>((ref) {
return CountChangeNotifier();
});
//読み込む
int countChangeNotifier = ref.watch(countChangeNotifierProvider).count;
//値を変更(1)
ref.read(countChangeNotifierProvider).increment();
//値を変更(2)
ref.read(countChangeNotifierProvider).count++;
はい。StateNotifierProvider
にとても似ていますが、これまでと異なりProvider自体が型を管理していません。
基本的には使わないことをおすすめします。
まとめ
- Provider(公式)
- 代入不可(決めた処理に沿った値が出力される)
- 他のProviderの値を処理してから出力するなどの用途で使える。
- 一時的なキャッシュとして利用することでパフォーマンスも向上できる。
- StateProvider(公式)
- 代入可能
- 変数のように利用したい時に便利。シンプル。
- StateNotifierProvider(公式)
- メゾット経由で更新できる。
-
StateNotifier
で指定した値を出力する。 - メゾット等を持たせることができるので、可変なものを管理するのに便利。
- FutureProvider(公式)
- 代入不可
- 非同期なもので利用すると便利。
-
watch
すると、AsyncValue
が返ってくる。
- StreamProvider(公式)
- 代入不可
- FutureProviderのStream版
- FirebaseやWebSocket等で使うととても便利。
-
watch
すると、AsyncValue
が返ってくる。
-
ChangeNotifierProvider(非推奨)(公式)- 代入とメゾット経由の更新が両方できる。
-
watch
すると、ChangeNotifier
が返ってくる。 - 可能な限り、
StateNotifierProvider
を使おう。
よろしければLGTMお願いします!!
サンプルコード
今回利用した6つのProviderを使ったカウンターアプリです。
-
Provider
はStateNotifierProvider
を監視して、2倍の値を表示します。 -
FutureProvider
は5秒後に5秒経過しました!と表示します。 -
StreamProvider
は1秒ごとに0-100の乱数を表示します。
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
///Provider
final doubleCountProvider = Provider<int>((ref) {
final count = ref.watch(countStateNotifierProvider);
return count * 2;
});
///StateProvider
final countStateProvider = StateProvider<int>((ref) => 0);
///StateNotifierProvider
class CountStateNotifier extends StateNotifier<int> {
CountStateNotifier() : super(0);
void increment() => state++;
}
final countStateNotifierProvider =
StateNotifierProvider<CountStateNotifier, int>((ref) {
return CountStateNotifier();
});
///FutureProvider
final countFutureProvider = FutureProvider<String>((ref) async {
await Future.delayed(const Duration(seconds: 5));
return "5秒経過しました!";
});
///StreamProvider
Stream<int> randomValueStream() async* {
final random = Random();
while (true) {
await Future.delayed(const Duration(seconds: 1));
yield random.nextInt(100);
}
}
final countStreamProvider = StreamProvider<int>((ref) => randomValueStream());
///ChangeNotifierProvider
class CountChangeNotifier extends ChangeNotifier {
int count = 0;
void increment() => count++;
}
final countChangeNotifierProvider =
ChangeNotifierProvider<CountChangeNotifier>((ref) {
return CountChangeNotifier();
});
class MyHomePage extends ConsumerWidget {
final String title;
const MyHomePage({Key? key, required this.title}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
//Provider
int doubleCount = ref.watch(doubleCountProvider);
//StateProvider
int countState = ref.watch(countStateProvider);
//StateNotifierProvider,StateNotifier
int countStateNotifier = ref.watch(countStateNotifierProvider);
//FutureProvider
AsyncValue<String> countFuture = ref.watch(countFutureProvider);
//SteamProvider
AsyncValue<int> countStream = ref.watch(countStreamProvider);
//ChangeNotifierProvider
int countChangeNotifier = ref.watch(countChangeNotifierProvider).count;
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Table(
columnWidths: const <int, TableColumnWidth>{
0: FlexColumnWidth(0.5),
1: FlexColumnWidth(0.5),
},
children: [
TableRow(children: [
const Text("Provider"),
Text("$doubleCount"),
]),
TableRow(children: [
const Text("StateProvider"),
Text("$countState"),
]),
TableRow(children: [
const Text("StateNotifierProvider"),
Text("$countStateNotifier"),
]),
TableRow(children: [
const Text("FutureProvider"),
Text(countFuture.when(
data: (String data) => data,
error: (_, __) => "error",
loading: () => "loading")),
]),
TableRow(children: [
const Text("StreamProvider"),
Text(countStream.when(
data: (int data) => "$data",
error: (_, __) => "error",
loading: () => "loading")),
]),
TableRow(children: [
const Text("ChangeNotifierProvider"),
Text("$countChangeNotifier"),
]),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
ref.read(countStateNotifierProvider.notifier).increment();
ref.read(countStateProvider.notifier).update((state) => state + 1);
ref.read(countChangeNotifierProvider).count++;
},
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}