👻

【Flutter】riverpod_generator 動作確認用コードサンプル

2023/01/21に公開

Riverpod の各プロバイダのサポート状況

「Riverpod の各プロバイダ」の 「riverpod_generator でのサポート状況」について、表にまとめます。更新履歴[1]

No. プロバイダ サポート状況 備考
1 Provider サポート済
2 NotifierProvider サポート済
3 AsyncNotifierProvider サポート済
4 StateNotifierProvider サポート予定無し(StateNotifier) (Async)NotifierProvider で代替?
5 FutureProvider サポート済
6 StreamProvider サポート済 2023/03/01? v2.0.0 で対応済
7 StateProvider サポート予定無し(StateProvider) [2]
8 ChangeNotifierProvider サポート予定無し(ChangeNotifier) (Async)NotifierProvider で代替?

各プロバイダの動作確認用コードサンプル

Provider

Provider

test/riverpod_generator/provider_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'provider_test.g.dart';


String Function(String s) hello(HelloRef ref) {
  String hello(String s) {
    return "Hello World $s";
  }

  return hello;
}


String Function(String s) helloF(HelloFRef ref, {required String init}) {
  String hello(String s) {
    return "$init $s";
  }

  return hello;
}

void main() {
  group("basic", () {
    test("Provider", () {
      final container = ProviderContainer();

      expect(helloProvider, isA<AutoDisposeProvider>());

      final hello = container.read(helloProvider);

      expect(hello("add"), "Hello World add");
    });
  });

  group("family", () {
    test("Provider", () {
      final container = ProviderContainer();

      expect(helloFProvider, isA<HelloFFamily>());

      final hello = container.read(helloFProvider(init: "Hello World"));

      expect(hello("add"), "Hello World add");
    });
  });
}
NotifierProvider

NotifierProvider

test/riverpod_generator/notifier_provider_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'notifier_provider_test.g.dart';


class HelloNotifier extends _$HelloNotifier {
  
  String build() {
    return "Hello World";
  }

  void add(String s) {
    state = "$state $s";
  }
}


class HelloNotifierF extends _$HelloNotifierF {
  
  String build({required String init}) {
    return init;
  }

  void add(String s) {
    state = "$state $s";
  }
}

void main() {
  group("basic", () {
    test("NotifierProvider build", () {
      final container = ProviderContainer();

      expect(helloNotifierProvider, isA<AutoDisposeNotifierProvider>());
      expect(container.read(helloNotifierProvider), "Hello World");
    });

    test("NotifierProvider add", () {
      final container = ProviderContainer();

      expect(helloNotifierProvider, isA<AutoDisposeNotifierProvider>());
      expect(container.read(helloNotifierProvider), "Hello World");

      container.read(helloNotifierProvider.notifier).add("add");
      expect(container.read(helloNotifierProvider), "Hello World add");
    });
  });

  group("family", () {
    test("NotifierProvider build", () {
      final container = ProviderContainer();

      expect(helloNotifierFProvider, isA<HelloNotifierFFamily>());
      expect(container.read(helloNotifierFProvider(init: "Hello World")),
          "Hello World");
    });

    test("NotifierProvider add", () {
      final container = ProviderContainer();

      expect(helloNotifierFProvider, isA<HelloNotifierFFamily>());
      expect(container.read(helloNotifierFProvider(init: "Hello World")),
          "Hello World");

      container
          .read(helloNotifierFProvider(init: "Hello World").notifier)
          .add("add");

      expect(container.read(helloNotifierFProvider(init: "Hello World")),
          "Hello World add");
    });
  });
}

AsyncNotifierProvider

AsyncNotifierProvider

test/riverpod_generator/async_notifier_provider_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'async_notifier_provider_test.g.dart';


class HelloAsyncNotifier extends _$HelloAsyncNotifier {
  
  Future<String> build() {
    return Future.value("Hello AsyncNotifier");
  }

  void add(String s) async {
    state = const AsyncLoading();
    state = await AsyncValue.guard(() => Future.value("${state.value} $s"));
  }
}


class HelloAsyncNotifierF extends _$HelloAsyncNotifierF {
  
  Future<String> build({required String init}) {
    return Future.value(init);
  }

  void add(String s) async {
    state = const AsyncLoading();
    state = await AsyncValue.guard(() => Future.value("${state.value} $s"));
  }
}

void main() {
  group("basic", () {
    test("AsyncNotifierProvider build", () async {
      final container = ProviderContainer();

      expect(
          helloAsyncNotifierProvider, isA<AutoDisposeAsyncNotifierProvider>());

      expect(container.read(helloAsyncNotifierProvider), isA<AsyncLoading>());

      // AsyncLoding から AsyncDataになるのを待つ
      await Future<void>.value();

      expect(
          container.read(helloAsyncNotifierProvider), isA<AsyncData<String>>());
      expect(container.read(helloAsyncNotifierProvider).value,
          "Hello AsyncNotifier");
    });
    test("AsyncNotifierProvider add", () async {
      final container = ProviderContainer();

      expect(
          helloAsyncNotifierProvider, isA<AutoDisposeAsyncNotifierProvider>());

      expect(container.read(helloAsyncNotifierProvider), isA<AsyncLoading>());

      await Future<void>.value();

      expect(
          container.read(helloAsyncNotifierProvider), isA<AsyncData<String>>());
      expect(container.read(helloAsyncNotifierProvider).value,
          "Hello AsyncNotifier");

      container.read(helloAsyncNotifierProvider.notifier).add("add");

      expect(container.read(helloAsyncNotifierProvider), isA<AsyncLoading>());

      // AsyncLoding から AsyncDataになるのを待つ
      await Future<void>.value();

      expect(
          container.read(helloAsyncNotifierProvider), isA<AsyncData<String>>());
      expect(container.read(helloAsyncNotifierProvider).value,
          "Hello AsyncNotifier add");
    });
  });

  group("family", () {
    test("AsyncNotifierProvider build", () async {
      final container = ProviderContainer();

      expect(helloAsyncNotifierFProvider, isA<HelloAsyncNotifierFFamily>());

      expect(
          container
              .read(helloAsyncNotifierFProvider(init: "Hello AsyncNotifier")),
          isA<AsyncLoading>());

      // AsyncLoding から AsyncDataになるのを待つ
      await Future<void>.value();

      expect(
          container
              .read(helloAsyncNotifierFProvider(init: "Hello AsyncNotifier")),
          isA<AsyncData<String>>());

      expect(
          container
              .read(helloAsyncNotifierFProvider(init: "Hello AsyncNotifier"))
              .value,
          "Hello AsyncNotifier");
    });

    test("AsyncNotifierProvider add", () async {
      final container = ProviderContainer();

      expect(helloAsyncNotifierFProvider, isA<HelloAsyncNotifierFFamily>());

      expect(
          container
              .read(helloAsyncNotifierFProvider(init: "Hello AsyncNotifier")),
          isA<AsyncLoading>());

      await Future<void>.value();

      expect(
          container
              .read(helloAsyncNotifierFProvider(init: "Hello AsyncNotifier")),
          isA<AsyncData<String>>());

      expect(
          container
              .read(helloAsyncNotifierFProvider(init: "Hello AsyncNotifier"))
              .value,
          "Hello AsyncNotifier");

      container
          .read(
              helloAsyncNotifierFProvider(init: "Hello AsyncNotifier").notifier)
          .add("add");

      expect(
          container
              .read(helloAsyncNotifierFProvider(init: "Hello AsyncNotifier")),
          isA<AsyncLoading>());

      // AsyncLoding から AsyncDataになるのを待つ
      await Future<void>.value();

      expect(
          container
              .read(helloAsyncNotifierFProvider(init: "Hello AsyncNotifier")),
          isA<AsyncData<String>>());
      expect(
          container
              .read(helloAsyncNotifierFProvider(init: "Hello AsyncNotifier"))
              .value,
          "Hello AsyncNotifier add");
    });
  });
}

FutureProvider

FutureProvider

test/riverpod_generator/future_provider_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'future_provider_test.g.dart';


Future<String> helloFuture(HelloFutureRef ref) {
  return Future.value("Hello Future");
}


Future<String> helloFutureF(HelloFutureFRef ref, {required String init}) {
  return Future.value("Hello Future $init");
}

void main() {
  group("basic", () {
    test("FutureProvider", () async {
      final container = ProviderContainer();

      expect(helloFutureProvider, isA<AutoDisposeFutureProvider>());
      expect(container.read(helloFutureProvider), isA<AsyncLoading>());

      // AsyncLoding から AsyncDataになるのを待つ
      await Future<void>.value();

      expect(
        container.read(helloFutureProvider),
        isA<AsyncData<String>>().having((s) => s.value, "", "Hello Future"),
      );

      expect(container.read(helloFutureProvider).value, "Hello Future");
    });
  });

  group("family", () {
    test("FutureProvider", () async {
      final container = ProviderContainer();

      const init = "Initial";

      expect(helloFutureFProvider, isA<HelloFutureFFamily>());
      expect(container.read(helloFutureFProvider(init: init)),
          isA<AsyncLoading>());

      // AsyncLoding から AsyncDataになるのを待つ
      await Future<void>.value();

      expect(
        container.read(helloFutureFProvider(init: init)),
        isA<AsyncData<String>>()
            .having((s) => s.value, "", "Hello Future $init"),
      );

      expect(container.read(helloFutureFProvider(init: init)).value,
          "Hello Future $init");
    });
  });
}

StreamProvider

StreamProvider

test/riverpod_generator/stream_provider_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'stream_provider_test.g.dart';


Stream<String> helloStream(HelloStreamRef ref) {
  return Stream.value("Hello Stream");
}


Stream<String> helloStreamF(HelloStreamFRef ref, {required String init}) {
  return Stream.value("Hello Stream $init");
}

void main() {
  group("basic", () {
    test("StreamProvider", () async {
      final container = ProviderContainer();

      expect(helloStreamProvider, isA<AutoDisposeStreamProvider>());
      expect(container.read(helloStreamProvider), isA<AsyncLoading>());

      // AsyncLoding から AsyncDataになるのを待つ
      await Future<void>.value();

      expect(
        container.read(helloStreamProvider),
        isA<AsyncData<String>>().having((s) => s.value, "", "Hello Stream"),
      );

      expect(container.read(helloStreamProvider).value, "Hello Stream");
    });
  });

  group("family", () {
    test("StreamProvider", () async {
      final container = ProviderContainer();

      const init = "Initial";

      expect(helloStreamFProvider, isA<HelloStreamFFamily>());
      expect(container.read(helloStreamFProvider(init: init)),
          isA<AsyncLoading>());

      // AsyncLoding から AsyncDataになるのを待つ
      await Future<void>.value();

      expect(
        container.read(helloStreamFProvider(init: init)),
        isA<AsyncData<String>>()
            .having((s) => s.value, "", "Hello Stream $init"),
      );

      expect(container.read(helloStreamFProvider(init: init)).value,
          "Hello Stream $init");
    });
  });
}

その他

非 AutoDispose 化

keepAlivetrueを指定すると、非 AutoDispose になるようです。

-    @riverpod
+    @Riverpod(keepAlive: true)

build.yaml

test/riverpod_generator/配下でコード生成するために設定しました。

build.yaml
targets:
  $default:
    sources:
      include:
        - $package$
        - pubspec.yaml
        - lib/**
        - test/riverpod_generator/**

思ったことメモ

  • StateProvider は、これまで StateNotifierProvider で代替可能だったように、今後は、(Async)NotifierProvider でも代替可能だと思われます。一方で、StateProvider は、class 定義せずに気軽に使える点にメリットを感じているため、代替せずに利用継続していくのも良いのではと思いました。

  • プロバイダが自動生成されるため、例えば、「NotifierProvider の class 定義」と、その class 定義から「自動生成されるプロバイダのインスタンス」が、1:1 になるように思われる。
    1:多、になるようにプロバイダのインスタンスを複数生成したい場合(この場合があるのか?)、どうやるか?

脚注
  1. 更新履歴
    2023/01/21:初回投稿
    2023/03/09:最新化 ↩︎

  2. StateProvider は十分シンプルなため、ジェネレータで自動生成する必要が無いため、サポートしない?。https://github.com/rrousselGit/riverpod/discussions/1725 ↩︎

Discussion