Kodokon kodokon.com

shared_preferencesによるローカルな永続化

shared_preferencesで設定や軽量なデータを永続化しつつ、そのセキュリティと容量の限界を知っておきましょう。

8 分 · 3 問

このレッスンを Kodokon で開く

shared_preferencesは、ネイティブの仕組み(AndroidではSharedPreferences、iOSではUserDefaults)に支えられたキー/値ストアです。得意分野は小さなデータ、つまり選ばれたテーマ、オンボーディングを見たかどうか、最後にアクティブだったタブなどです。それを超えたら道具を変えましょう。大きなデータや問い合わせ可能なデータにはsqflitedriftのデータベースを、機密性のあるものにはflutter_secure_storageを使います。ファイルはまるごとメモリに読み込まれます。暗号化もされていなければ、大きくなるようにも作られていません。

BASH
flutter pub add shared_preferences

getInstance()は最初の呼び出しでは非同期です(ファイルを読むため)。それからキャッシュされたインスタンスを返します。以降のget*呼び出しは同期的で、メモリから提供されます。一方でset*呼び出しは非同期にディスクへ書き込みます。実際のところ、キーの文字列をアプリケーション中にばらまいてはいけません。型付きで、注入可能で、テスト可能なラッパーのなかに集約しましょう。

DART
import 'package:shared_preferences/shared_preferences.dart';

class SettingsStore {
  SettingsStore(this._prefs);

  static const _darkModeKey = 'settings.darkMode';

  final SharedPreferences _prefs;

  static Future<SettingsStore> load() async {
    final prefs = await SharedPreferences.getInstance();
    return SettingsStore(prefs);
  }

  bool get darkMode =>
      _prefs.getBool(_darkModeKey) ?? false;

  Future<void> setDarkMode(bool value) =>
      _prefs.setBool(_darkModeKey, value);
}
集約されたキー、保証された型、注入可能な依存関係。

このAPIはプリミティブ型しか保存しません。boolintdoubleString、そしてList<String>です。構造化されたオブジェクトには、JSONへシリアライズしましょう。最初のレッスンのtoJson/fromJsonがここでもまた役に立ちます。受け入れるべきトレードオフとして、保存のたびに値全体が書き換えられ、問い合わせはできません。下書きにはぴったりですが、履歴には向きません。

DART
import 'dart:convert';

import 'package:shared_preferences/shared_preferences.dart';

class Draft {
  const Draft({required this.title, required this.body});

  factory Draft.fromJson(Map<String, dynamic> json) =>
      Draft(
        title: json['title'] as String,
        body: json['body'] as String,
      );

  final String title;
  final String body;

  Map<String, dynamic> toJson() =>
      {'title': title, 'body': body};
}

Future<void> saveDraft(Draft draft) async {
  final prefs = await SharedPreferences.getInstance();
  await prefs.setString('draft', jsonEncode(draft));
}

Future<Draft?> loadDraft() async {
  final prefs = await SharedPreferences.getInstance();
  final raw = prefs.getString('draft');
  if (raw == null) return null;
  return Draft.fromJson(
    jsonDecode(raw) as Map<String, dynamic>,
  );
}
単純なJSON文字列として保存された完全なオブジェクト。

理解度チェック

このレッスンの要点をしっかり覚えているか確認しましょう。

  1. どのデータがshared_preferencesに属しますか?
    • ユーザーの認証トークン。
    • ユーザーの5万件の取引の完全な履歴。
    • ダークテーマの選択。
  2. 最初のgetInstance()のあと、get*の読み取りはどう振る舞いますか?
    • 読み取りのたびにディスクからファイルを読み直す。
    • 同期的である。値はメモリのキャッシュから提供される。
    • 毎回awaitしなければならないFutureを返す。
  3. 構造化されたDraftオブジェクトをどう保存しますか?
    • jsonEncodeでJSONへシリアライズし、setStringで保存する。
    • この目的のために設計されたprefs.setObject('draft', draft)を使う。
    • フィールドごとに分けてsetStringListに入れる。