🍯

AutoDisposeAsyncNotifierを使ってみる

2023/12/17に公開

Overview

最近仕事で、FutureProviderを使わずに、AsyncValueのデータ型のStateNotifierを使う機会があって、もしかしたら、riverpod2.0のAsyncNotifierでも同じようなことをできるのではと思ってやってみた。

こちらが公式のリファレンス:
https://pub.dev/documentation/riverpod/latest/riverpod/AsyncNotifier-class.html

非同期的に初期化される 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