💡

起動時にRiverpodに初期値を設定する実装

2024/01/22に公開

起動時にRiverpodに初期値を設定する実装

  • 下記の課題感があった

    • アプリ起動した際に、Riverpodにすでにデータが設定されている状態にしたい
  • 具体的には、

    • FutureProviderで対応していたが、非asyncなProviderで楽に取り扱いたい
      UI側でのFutureを減らしたい
    • sharedPreferenceをProvider管理していたがasyncなのが嫌だった

対応概要

対応する際のポイントは下記

- 1, main()で初期値用の一時変数を定義
- 2, main()で初期値取得(Future等)し一時変数に格納
- 3, runApp(ProviderScope()) でProviderに値を設定

実装

・色のThemeと、言語のLanguageを、Providerで管理している場合の例。
(※自身のアプリに組み込む際は、「一時変数定義」「初期値取得処理」「上書き更新」を適宜修正)

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  // - 1, main()で初期値用の一時変数を定義 ---

  // Future処理後に、値を設定するため late で定義
  late final ThemeMode initialLoadedTheme;
  late final Language initialLoadedLanguage;
  
  // - 2, main()で初期値取得(Future等)し一時変数に格納 ---
  
  // await Future.wait([])は、複数の非同期処理を並行して実行し、それらが全て完了するのを待つことが可能
  // 処理順が関係ない初期値の取得処理をまとめて実行
  // また、並列処理なので時間の短縮となる利点がある
  await Future.wait(
    [                  
      Future(() async {
        // MEMO: 起動時に、Themeを取得し、【ProviderScope -> overides で、設定】
        initialLoadedTheme = await ThemeSelectorExtension.loadedState();
      }),

      Future(() async {
        // MEMO: 起動時に、言語設定を取得し、【ProviderScope -> overides で、設定】
        initialLoadedLanguage = await LaungageSelectorExtension.loadedState();
      }),      
    ]
  );
}

// - 3, runApp(ProviderScope()) でProviderに値を設定 ---      

runApp(
  ProviderScope(
    overrides: [            
      // 該当Porviderに、main()で取得した値を設定(override利用)
      themeSelectorProvider.overrideWith((ref) => ThemeSelector(initialLoadedTheme),),
      languageSelectorProvider.overrideWith((ref) => LanguageSelector(initialLoadedLanguage),),
    ],
    child: MyApp()
  )
);

対応コードの解説

- 1, main()で初期値取得し一時変数に格納
  • main() -> Future<void> main() async に変更
  • final late で、一時変数を用意
  • await Future.await([])でFuture処理を実行
    ※もし、1つしかFuture処理がなくても、今後の追加が楽なので使っておいた方が楽かも
- 2, main()で初期値取得(Future等)し一時変数に格納 ---
  • await Future.wait([]) 内で、Future処理を実行しデータ取得
- 3, runApp(ProviderScope()) でProviderに値を設定
  • overrider: [] のなかで任意のProviderの初期値を設定
  • Provider.overrideWith() で、上書き更新処理で対応

備考

※loadedState()の取得処理の中身は、SharedPreferenceから値を取得している処理です。

  /// 現在選択中の言語設定を`SharedPreferences`から取得
  Future<Language> loadedState async {
    final prefs = await SharedPreferences.getInstance();
    final langStr = prefs.getString("languagePrefsKey");
    final langEnum = LanguageExtension.whichLanguage(languageStr: langStr ?? "");
    return langEnum;
  }

注意点

もし初回Futureの処理が重い(長い)場合、「アプリ起動 -> スプラッシュ」の時間が長くなります。

これを嫌う場合は、素直に初回画面起動した後に、「ローディング表示(※)」で対応が良いと思います。

ローディングUI実装

https://zenn.dev/suzuki_kawasaki/articles/18b11e46c81457

Discussion