Kodokon kodokon.com

Lokale Persistenz mit shared_preferences

Speichere Einstellungen und leichte Daten dauerhaft mit shared_preferences und kenne dabei die Grenzen bei Sicherheit und Datenmenge.

8 Min. · 3 Fragen

Diese Lektion in Kodokon öffnen

shared_preferences ist ein Schlüssel/Wert-Speicher, der auf nativen Mechanismen aufsetzt (SharedPreferences unter Android, UserDefaults unter iOS). Seine Stärke: kleine Daten - gewähltes Theme, Onboarding gesehen, zuletzt aktiver Tab. Alles darüber hinaus verlangt ein anderes Werkzeug: eine sqflite- oder drift-Datenbank für große oder abfragbare Daten, flutter_secure_storage für alles Sensible. Die Datei wird komplett in den Arbeitsspeicher geladen: Sie ist weder verschlüsselt noch dafür gebaut zu wachsen.

BASH
flutter pub add shared_preferences

getInstance() ist beim ersten Aufruf asynchron (Lesen der Datei) und liefert danach eine zwischengespeicherte Instanz: Die folgenden get*-Aufrufe sind synchron und werden aus dem Speicher bedient. Die set*-Aufrufe schreiben dagegen asynchron auf die Festplatte. In der Praxis: Streue keine Schlüssel-Strings über die ganze Anwendung, sondern zentralisiere sie in einem typisierten, injizierbaren und testbaren Wrapper.

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);
}
Zentralisierte Schlüssel, garantierte Typen, injizierbare Abhängigkeit.

Die API speichert nur primitive Typen: bool, int, double, String und List<String>. Für ein strukturiertes Objekt serialisierst du es nach JSON - deine toJson/fromJson aus der ersten Lektion kommen hier wieder zum Einsatz. Ein akzeptierter Kompromiss: Jedes Speichern schreibt den kompletten Wert neu und Abfragen sind nicht möglich. Perfekt für einen Entwurf, ungeeignet für einen Verlauf.

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>,
  );
}
Ein komplettes Objekt, gespeichert als einfache JSON-Zeichenkette.

Wissenscheck

Stelle sicher, dass du die wichtigsten Punkte dieser Lektion behalten hast.

  1. Welche Daten gehören in shared_preferences?
    • Das Authentifizierungstoken der Nutzerin.
    • Der komplette Verlauf von 50.000 Transaktionen der Nutzerin.
    • Die Wahl des dunklen Themes.
  2. Wie verhalten sich die get*-Lesevorgänge nach dem ersten getInstance()?
    • Jeder Lesevorgang liest die Datei erneut von der Festplatte.
    • Sie sind synchron: Die Werte werden aus einem Speicher-Cache bedient.
    • Sie liefern Futures zurück, auf die du jedes Mal warten musst.
  3. Wie speicherst du ein strukturiertes Draft-Objekt?
    • Serialisiere es mit jsonEncode nach JSON und speichere es über setString.
    • Verwende prefs.setObject('draft', draft), das genau dafür gedacht ist.
    • Teile es Feld für Feld in eine setStringList auf.