AutoDisposeAsyncNotifierを使ってみる
Overview
最近仕事で、FutureProviderを使わずに、AsyncValueのデータ型のStateNotifierを使う機会があって、もしかしたら、riverpod2.0のAsyncNotifierでも同じようなことをできるのではと思ってやってみた。
こちらが公式のリファレンス:
非同期的に初期化される Notifier 実装。
これは FutureProvider に似ていますが、パブリック メソッドを定義することで副作用を実行できます。
一般的に次の目的で使用されます。
ネットワーク要求をキャッシュしながら、副作用の実行も可能にします。 たとえば、build は現在の「ユーザー」に関する情報を取得できます。 また、AsyncNotifier は「setName」などのメソッドを公開して、現在のユーザー名を変更できるようにする可能性があります。
非同期データ ソースから Notifier を初期化します。 たとえば、Notifier の初期状態をローカル データベースから取得します。
autoDispose またはファミリーを使用すると、通知タイプが変わります。 AsyncNotifier を拡張する代わりに、次のいずれかを拡張する必要があります。
- AutoDisposeAsyncNotifier for autoDispose
- FamilyAsyncNotifier for family
- AutoDisposeFamilyAsyncNotifier for autoDispose.family
summary
早速機能を実装してみた。やってることは単純で、Firestoreのuserコレクションから、データを一度だけ取得するQuerySnapshot
を使ってるだけです。
これは多分AsyncValueを使ったStateNotifierと同じようなものだと思われます??
AsyncNotifier
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'main_view_model.g.dart';
class MainAsyncNotifier extends _$MainAsyncNotifier {
FutureOr<QuerySnapshot> build() {
return getDocuments();
}
Future<QuerySnapshot> getDocuments() async {
final snapshot = await FirebaseFirestore.instance.collection('user').get();
return snapshot;
}
}
実行するコード
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:isige_app/firebase_options.dart';
import 'package:isige_app/main_view_model.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends ConsumerWidget {
const MyHomePage({super.key});
Widget build(BuildContext context, WidgetRef ref) {
// AsyncValue<QuerySnapshot<Object?>>でデータが返ってくる
final asyncValue = ref.watch(mainAsyncNotifierProvider);
return Scaffold(
appBar: AppBar(
title: const Text('Riverpod AsyncValue'),
),
body: Center(
child: asyncValue.when(
data: (data) {
return ListView.builder(
itemCount: data.docs.length,
itemBuilder: (context, index) {
// Map<String, dynamic>でデータが返ってくる
final docData = data.docs[index].data() as Map<String, dynamic>;
final name = docData['name'];
final email = docData['email'];
return ListTile(
title: Text(name),
subtitle: Text(email),
);
},
);
},
loading: () => const CircularProgressIndicator(),
error: (error, stackTrace) => Text(error.toString()),
),
),
);
}
}
実行結果:
thoughts
FutureOr<>っていうのが何か気になって内部のコードを見て見ました。今回は情報が世の中にない中色々と探求して見ました。
もっと良さそうな使い道がありそうなので、色々探求してみたいです。
このコードはDartのFuture<T>クラスを定義しています。Future<T>は非同期プログラミングにおいて、未来のある時点で利用可能になる値(またはエラー)を表します。
("wasm:entry-point")
abstract interface class Future<T> {
/// `null`で完了した`Future<Null>`。
///
/// 現在は`dart:internal`と共有されています。
/// そのFutureが削除できるなら、これを`_Future<Null>.zoneValue(null, _rootZone);`に戻します。
static final _Future<Null> _nullFuture = nullFuture as _Future<Null>;
/// `false`で完了した`Future<bool>`。
static final _Future<bool> _falseFuture =
new _Future<bool>.zoneValue(false, _rootZone);
/// [computation]を非同期に呼び出す結果を含むFutureを作成します。
///
/// [computation]の実行結果が例外を投げる場合、返されるFutureはそのエラーで完了します。
///
/// 返される値が自身が[Future]である場合、作成されたFutureの完了は返されたFutureが完了するまで待ちます、
/// そしてその後、同じ結果で完了します。
///
/// 非Future値が返される場合、返されるFutureはその値で完了します。
factory Future(FutureOr<T> computation()) {
_Future<T> result = new _Future<T>();
Timer.run(() {
try {
result._complete(computation());
} catch (e, s) {
_completeWithErrorCallback(result, e, s);
}
});
return result;
}
Discussion