アプリの起動時にRiverPodからメソッドを呼びたい

2023/01/01に公開

何がしたいか

main.dartでRiverPodを使いたいけど、refが取れない問題に直面しました。
例えばこんな状況で

  • アプリの起動時にユーザの登録状況を判定して初回動線を変更したい。
  • アプリの起動時にプッシュ通知の受信状況をlistenしておきたい。
  • デープリンクの受信をlistenしておきたい。
  • 認証状況を〜

結論から言うとこちらの記事が大変参考になりました。
https://codewithandrea.com/articles/riverpod-initialize-listener-app-startup/

参考記事にはlistenの方法が載ってますので、ここでは「ユーザの登録状況を判定して初回動線を変更したい」をサンプルにコードを載せたいと思います。

例えばの仕様

  • 利用規約に同意したユーザor同意していないユーザかをアプリ起動時に判定する。
  • 同意済のユーザはhome画面へ、同意していないユーザは利用規約同意画面へ遷移させる。

初期起動時に行う処理

app_launching_repository.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hogehoge/data/api/api.dart';
import 'package:hogehoge/router/router.dart';

class AppLaunchingRepository {
  AppLaunchingRepository(this._api);

  final Api _api;

  Future<String> setInitialRouting() async {
    try {

      // もう少しちゃんと作ると会員か非会員かなどのチェックがあったり、
      // ログインのセッションが有効か無効かな分岐があったりしますが割愛します。

            // ここでAPIを呼び出すときは、端末内に保存している情報を送ったりしてます。
      final user = await _api.getUser();
      if (!user.agreement) {
	  return Routers.termsAgreement;
      }

      return Routers.home;
    } on Exception {
      return Routers.login;
    }
  }
}

final appLaunchingRepositoryProvider = Provider<AppLaunchingRepository>((ref) {
  final api = ref.read(apiProvider);
  return AppLaunchingRepository(api);
});
api.dart
()
abstract class Api {
  factory Api(Dio dio) = _Api;

  ('/user')
  Future<Response> getUser();
}

final apiProvider = Provider<Api>((ref) {
  final dio = ref.read(dioProvider);
  return Api(dio);
});
dio.dart
final dioProvider = Provider<Dio>((ref) {
  final bc = ref.read(buildConfigProvider);
  final dio = Dio(
    BaseOptions(
      baseUrl: '${bc.baseUrl}/api/v1',
    ),
  );
  // 省略
  return dio;
});

main.dartで呼び出す

main.dart
void main() async {
  final widgetsBinding = WidgetsFlutterBinding.ensureInitialized();

  // ProviderContainerをfinalで取る。(しれっとdynamic linkのlisten処理も呼んでます。)
  final providerContainer =
      ProviderContainer()
        ..read(dynamicLinkEventProvider);

  // アプリ初回起動処理を呼ぶ、初回起動時のルートパスを取得(例)
  final initialRoute = await providerContainer
      .read(appLaunchingRepositoryProvider)
      .setInitialRouting();

  runApp(
    ProviderScope(
       // parentにproviderContainerをセット
      parent: providerContainer,
      child: App(
        initialRoute: initialRoute,
      ),
    ),
  );
}

ProviderContainer

https://pub.dev/documentation/riverpod/latest/riverpod/ProviderContainer-class.html

river_podで宣言したproviderの状態を格納しているもの。
中身をオーバーライドできるので、テスト時に使用したりする。
その他、実際のPJではdart-defineで環境を切り替えているので、起動時引数を判定して特定のProviderをoverrideして使用していたりします。(APIの向き先などを切り替えたりなど)

おわり

Discussion