احفظ التفضيلات والبيانات الخفيفة بشكل دائم باستخدام shared_preferences مع معرفة حدوده الأمنية وحدود الحجم.
افتح هذا الدرس في Kodokonإن shared_preferences مخزن مفتاح/قيمة مدعوم بآليات أصلية (SharedPreferences على أندرويد، وUserDefaults على iOS). ومجاله المثالي: البيانات الصغيرة - السمة المختارة، والتعريف التمهيدي (onboarding) الذي شُوهد، وآخر تبويب نشط. وفيما وراء ذلك، غيِّر الأدوات: قاعدة بيانات sqflite أو drift للبيانات الكبيرة أو القابلة للاستعلام، وflutter_secure_storage لأيّ شيء حسّاس. ويُحمَّل الملف بأكمله في الذاكرة: فهو غير مُشفَّر ولا مبنيّ للنمو.
flutter pub add shared_preferencesإن getInstance() لاتزامني عند الاستدعاء الأول (قراءة الملف)، ثم يُرجِع نسخة مخزّنة مؤقتًا: فاستدعاءات get* اللاحقة تزامنية، تُقدَّم من الذاكرة. أما استدعاءات set* فتكتب على القرص بشكل لاتزامني. وعمليًا، لا تنثر سلاسل المفاتيح في أرجاء التطبيق كله: بل اجمعها في غلاف (wrapper) ذي أنواع، وقابل للحقن، وقابل للاختبار.
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);
}لا تخزّن الواجهة البرمجية سوى الأنواع الأولية: 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 وخزِّنه عبر setString.prefs.setObject('draft', draft) المصمَّم لهذا الغرض.setStringList.