Сохраняй настройки и лёгкие данные через shared_preferences, зная его ограничения по безопасности и объёму.
Открыть этот урок в Kodokonshared_preferences - это хранилище ключ/значение поверх нативных механизмов (SharedPreferences на Android, UserDefaults на iOS). Его зона комфорта: небольшие данные - выбранная тема, пройденный онбординг, последняя активная вкладка. Дальше меняй инструмент: база 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?get* после первого getInstance()?Future, который надо ждать каждый раз.Draft?jsonEncode и сохранить через setString.prefs.setObject('draft', draft), придуманный для этого.setStringList.