【Flutter】 Riverpod チートシート(書きかけ)
背景
状態管理のプラグインとして、Riverpodを使ってます。そこそこ使えるようになりましたが、(年のせいか、元々記憶力がないだけなのか)細かいところが思い出せない。そのため、今回Riverpod関連が一覧で見られるチートシートを作ろうと思いました。テスト用の実装として、FlutterのテンプレートのカウントアップアプリをRiverpodの各Providerで実装しました。簡単ではありますが、参考になると思います。各Providerの最後の「ソース」から飛べます。
あくまで解っている人向けのチートシートなので、もうちょっと細かい説明を「本」として出したいと思っております。
実施環境
Riverpodで破壊的な変更が(人知れず)起こっているので、見つけたらご連絡ください。一応、気をつけてはいるのです、、、
(追記) 2021/11/05、1.0.0がリリースされました!それに合わせて、修正しました。
flutter_riverpod: ^1.0.0
[√] Flutter (Channel stable, 2.5.3, on Microsoft Windows [Version 10.0.19043.1288], locale ja-JP)
[√] Android toolchain - develop for Android devices (Android SDK version 31.0.0)
[√] Android Studio (version 2020.3)
前準備
flutter pub add flutter_riverpod
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
- runApp(const MyApp());
+ runApp(ProviderScope(child: const MyApp()));
}
Provider
Provider 定数
- 定義
Provider<String> _provider =
Provider((ref) => 'You have pushed the button this many times:');
Provider<String> _provider =
Provider((ref) => ref.watch(anotherProider).state * 2);
- 表示
ref.watch(_provider)
-
変更
できない -
使用用途
定数に対して使う。ほかにも、他のProviderの値を受けて、値を表示することもできる。
StateProvider 変数
- 定義
StateProvider<int> _stateProvider = StateProvider((ref) => 0);
- 表示
ref.watch(_stateProvider)
- 変更
どれを使うべきなのだろうか、、
ref.read(_stateProvider.state).state++;
ref.read(_stateProvider.state).state = ref.read(_stateProvider) + 1;
ref.read(_stateProvider.state).update((state) => state + 1);
ref.read(_stateProvider.notifier).state++;
ref.read(_stateProvider.notifier).state = ref.read(_stateProvider) + 1;
ref.read(_stateProvider.notifier).update((state) => state + 1);
- 使用用途
riverpodのProviderの中で基本。intやクラスなどの値を管理する。
StateNotifier 変数 + メソッド
- 定義
class CounterNotifier extends StateNotifier<int> {
CounterNotifier() : super(0);
void countUp() {
state++;
}
}
final _provider =
StateNotifierProvider<CounterNotifier, int>((ref) => CounterNotifier());
- 表示
Text(
'${ref.watch(_provider)}',
style: Theme.of(context).textTheme.headline4,
),
- 変更
ref.watch(_provider.notifier).countUp(),
- 使用用途
メソッドがあるので、MVVMモデルっぽく使う方がいる。(個人的には、VMは一つのクラスとして独立させた方が良いと考えている)
ChangeNotifierProvider
- 定義
class Counter extends ChangeNotifier {
int _counter = 0;
get counter => _counter;
void countUp() {
_counter++;
notifyListeners();
}
}
final _provider = ChangeNotifierProvider((ref) => Counter());
- 表示
ref.watch(_provider).counter
- 変更
ref.watch(_provider).countUp()
- 使用用途
ChangeNotifierが好きな人用?個人的には、notifyListeners()を書き忘れるので、苦手だったりします。
FutureProvier Futureを使う
- 定義
final _provider = FutureProvider<int>((ref) async {
final prefs = await SharedPreferences.getInstance();
return prefs.getInt(KEY) ?? 0;
});
- 表示
Widgetを返す
ref.watch(_provider).when(
data: (data) => Text(
'${ref.watch(_provider).value}',
style: Theme.of(context).textTheme.headline4,
),
loading: () => const CircularProgressIndicator(),
error: (error, stack) => Text('error'),
),
- 変更
onPressed: () async {
final prefs = await SharedPreferences.getInstance();
int currentValue = prefs.getInt(KEY) ?? 0;
prefs.setInt(KEY, currentValue + 1);
ref.refresh(_provider);
},
- 使用用途
Future型を使用するので、SharedPreferencesだけでなく、WebAPIやファイル読込など使用するケースは多いと思われる。
StreamProvider Streamを使う
- 定義
StreamController<int> streamController = StreamController();
final _provider = StreamProvider<int>((ref) {
if (streamController.hasListener) {
streamController = StreamController();
}
return streamController.stream;
});
- 表示
Widgetを返す
ref.watch(_provider).when(
data: (data) => Text(
'${ref.watch(_provider).value}',
style: Theme.of(context).textTheme.headline4,
),
loading: () => const CircularProgressIndicator(),
error: (error, stack) => Text('${error}'),
),
- 変更
onPressed: () {
count++;
streamController.sink.add(count);
},
- 使用用途
Firestoreでsnapshotsを使用するケースが一番使えると思う。
表示するためのWidget
Consumer
通常のWidgetをラップし(この場合Text)、ラップしたWidget内でProviderでアクセスできるようになる。StatelessWidgetやStatefulWidgetのWidgetでriverpodを使用したい場合に使用する。
Consumer(
builder: (context, ref, child) => Text(
'${ref.watch(_stateProvider).state}',
style: Theme.of(context).textTheme.headline4,
),
)
ConsumerWidget
StatelessWidgetのriverpod版。全面的にriverpodを使っていくので、WidgetごとにConsumerで囲むのが厳しい場合に使用する。もしくは、riverpod専用のWidgetを作成する場合に使用する。
class MyHomePage extends ConsumerWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
Widget build(BuildContext context, WidgetRef ref) {
return Text(ref.watch(_provider));
}
}
プロバイダ修飾子
autoDispose
参照されなくなったプロバイダのステートを自動で破棄する場合に使用する。
final _provider = FutureProvider.autoDispose<int>((ref) async {
final prefs = await SharedPreferences.getInstance();
return prefs.getInt(KEY) ?? 0;
});
基本読み込まれたデータはそのまま使用される。しかし、refreshを使用することで、強制的にデータを再度読み込むことができる。
ref.refresh(_setMenuListProvider);
family
たとえばデータベースから、該当のIDのTODOのデータを取得するとき、IDをAPIの引数として渡します。引数を使用する場合は、familyを使用する。
以下、String?型を引数として、Todo型のデータを返します。(IDがない場合は空のTODOを、ある場合はデータベースから取得します)
定義
final _todoProvider =
FutureProvider.autoDispose.family<Todo, String?>((ref, id) async {
Todo todo = Todo(id: '', name: '');
if (id != null && id.isNotEmpty) {
var database = GetIt.I.get<Database>();
todo = await Todo.selectOne(database, id);
}
return todo;
});
利用
ref.watch(_todoProvider(id)).when(
error: (error, _) => Text(error.toString()),
loading: () => const EmProgressIndicator(),
data: (todo) => Text('${todo.id} ${}todo.name');
);
気に入っていただけましたら
Twitterで呟いていただいたり、ブログ記事等からリンクを貼っていただけると、非常に嬉しいです!また、間違いのご指摘や使用用途のアイデアがあれば、コメントをお願いします!
より詳しく学習するために
チートシートを前提としているため、カウントアップで状態管理の対象をintとさせていただきました。データクラスを対象としたい、WebAPIやFirestoreから取得したデータをRiverpodで扱いたい、という方がいると思います。その場合は、以下のUdemy講座をご利用ください。Riverpodの基本から、MVVMモデルへの応用、もちろん、WebAPIやFirestoreからのデータも取り扱っています。ぜひご受講ください。
クーポンコードもありますので、ご利用ください。
Flutter x Riverpod x MVVMで実現するシンプルな設計
Discussion