shared_preferencesで設定や軽量なデータを永続化しつつ、そのセキュリティと容量の限界を知っておきましょう。
このレッスンを Kodokon で開くshared_preferencesは、ネイティブの仕組み(AndroidではSharedPreferences、iOSではUserDefaults)に支えられたキー/値ストアです。得意分野は小さなデータ、つまり選ばれたテーマ、オンボーディングを見たかどうか、最後にアクティブだったタブなどです。それを超えたら道具を変えましょう。大きなデータや問い合わせ可能なデータにはsqfliteやdriftのデータベースを、機密性のあるものにはflutter_secure_storageを使います。ファイルはまるごとメモリに読み込まれます。暗号化もされていなければ、大きくなるようにも作られていません。
flutter pub add shared_preferencesgetInstance()は最初の呼び出しでは非同期です(ファイルを読むため)。それからキャッシュされたインスタンスを返します。以降のget*呼び出しは同期的で、メモリから提供されます。一方でset*呼び出しは非同期にディスクへ書き込みます。実際のところ、キーの文字列をアプリケーション中にばらまいてはいけません。型付きで、注入可能で、テスト可能なラッパーのなかに集約しましょう。
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はプリミティブ型しか保存しません。bool、int、double、String、そしてList<String>です。構造化されたオブジェクトには、JSONへシリアライズしましょう。最初のレッスンのtoJson/fromJsonがここでもまた役に立ちます。受け入れるべきトレードオフとして、保存のたびに値全体が書き換えられ、問い合わせはできません。下書きにはぴったりですが、履歴には向きません。
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>,
);
}shared_preferencesに属しますか?getInstance()のあと、get*の読み取りはどう振る舞いますか?Futureを返す。Draftオブジェクトをどう保存しますか?jsonEncodeでJSONへシリアライズし、setStringで保存する。prefs.setObject('draft', draft)を使う。setStringListに入れる。