Chapter 19

[v0.14.0以下版] SharedPreferencesのインスタンスをProviderでキャッシュする

村松龍之介
村松龍之介
2023.02.12に更新

アプリを作る上で、ユーザーが変更したアプリ内設定や、フラグなどの値を保存したいことは多いのではないでしょうか?

複数デバイスや複数ユーザーで共有したい設定はサーバーやクラウドDBなど、何かしらの記憶領域に保存する必要がありますが、
ローカル(ユーザーのデバイス上)に保存する場合は、「shared_preferences」が使われているのを多く見かけます。
https://pub.dev/packages/shared_preferences

iOSでは UserDefaults 、 Androidでは SharedPreferences をラップしたプラグイン(パッケージ)です。

保存できる型には制限がありますが、お手軽に使えて便利です。

サンプルリポジトリのリンク

https://github.com/Riscait/flutter_playground/blob/main/lib/src/top_level_providers/shared_preferences.dart

なぜProviderか

詳しくは後述しますが、 SharedPreferences のインスタンス取得は非同期です。
SharedPreferences を使いたいとき、毎回非同期でインスタンスを取得せずに使用するためにも、 Provider を使ってインスタンスをキャッシュします。

また、一度取得したインスタンスは基本的には一貫して使うもののため State も必要ありません。
なので変化する状態を持たない、一番基本的な「Provider」を使用します。

SharedPreferencesのインスタンス取得は非同期

SharedPreferencesのインスタンスを取得する方法は以下の通りです。

final prefs = await SharedPreferences.getInstance();

そう、インスタンスの取得が非同期処理となっています。

なので、これをそのままProviderでキャッシュして使おうとすると…

final sharedPreferencesProvider = FutureProvider((_) => SharedPreferences.getInstance());

のように FutureProvider を使わざるをえなくなってしまいます。

しかし、FutureProviderを使用すると呼び出すときに毎回非同期になってしまいます。
このままでは、SharedPreferencesで保存した値を読み込む get メソッドは非同期ではないのに、 Future で使わないといけなくなります。

非同期でのインスタンス取得は初回だけにする

まず以下のように、そのままアクセスすると例外を投げる(スローする)Providerを作成します。

final sharedPreferencesProvider = Provider<SharedPreferences>((_) => throw UnimplementedError());

main.dartmain() 関数を以下のように書き換えます。

main.dart
Future<void> main() async {
  // `runApp` 関数が終わる前に何か処理を実行する場合は `ensureInitialized()` メソッドを追記する必要がある
  WidgetsFlutterBinding.ensureInitialized();
  runApp(
    ProviderScope(
      overrides: [
        sharedPreferencesProvider.overrideWithValue(
          // ここでインスタンス化し、Providerの値を上書きします
          await SharedPreferences.getInstance(),
        ),
      ],
      child: App(),
    ),
  );
}

ProviderScopeのoverridersを使って値を上書きする

ProviderScopeでは、 overriders パラメータを使用して、Providerやその値を上書き(差し替え)することができます。

上記の例では、 sharedPreferencesProvider の値を 非同期で取得した SharedPreferences のインスタンスに差し替えています。

これで、SharedPreferencesのインスタンスをキャッシュして使用できるようになりました🎉