7 Min

Caching-Strategien in Flutter: lokaler Cache, veraltete Daten, Aktualisierungsregeln

Caching-Strategien in Flutter: was lokal speichern, wann invalidieren und wie man Bildschirme konsistent hält — Regeln für Frische, TTLs und Invalidation.

Caching-Strategien in Flutter: lokaler Cache, veraltete Daten, Aktualisierungsregeln

Warum Caching in Flutter-Apps knifflig wird

Caching in einer mobilen App bedeutet, eine Kopie von Daten in der Nähe zu halten (im Speicher oder auf dem Gerät), damit der nächste Bildschirm sofort rendern kann, statt auf das Netzwerk zu warten. Diese Daten können eine Liste von Items, ein Benutzerprofil oder Suchergebnisse sein.

Das Problem ist, dass gecachte Daten oft leicht falsch sind. Nutzer merken das schnell: ein Preis, der nicht aktualisiert wird, eine Badge-Anzeige, die sich festgefahren anfühlt, oder ein Detailbildschirm, der kurz nach einer Änderung alte Informationen zeigt. Was das Debuggen schwierig macht, ist das Timing. Derselbe Endpunkt kann nach Pull-to-Refresh in Ordnung aussehen, aber nach Zurück-Navigieren, App-Resume oder Account-Wechsel falsch.

Es gibt einen echten Kompromiss. Wenn du immer frische Daten holst, wirken Bildschirme langsam und springen, und du verschwendest Akku und Daten. Wenn du aggressiv cacht, fühlt sich die App schnell an, aber Nutzer hören auf, dem Gesehenen zu vertrauen.

Ein einfaches Ziel hilft: mache Frische vorhersehbar. Entscheide, was jeder Bildschirm zeigen darf (frisch, leicht veraltet oder offline), wie lange Daten leben dürfen, bevor du sie erneuerst, und welche Ereignisse sie ungültig machen müssen.

Stell dir einen typischen Ablauf vor: ein Nutzer öffnet eine Bestellung und geht dann zurück zur Bestellliste. Kommt die Liste aus dem Cache, zeigt sie möglicherweise noch den alten Status. Wenn du bei jedem Aufruf neu lädst, kann die Liste flackern und langsam wirken. Klare Regeln wie „zeige Cache sofort, aktualisiere im Hintergrund und aktualisiere beide Bildschirme, wenn die Antwort eintrifft“ machen das Erlebnis über Navigation hinweg konsistent.

Eine einfache Denke zu Cache und Frische

Ein Cache ist nicht nur "gespeicherte Daten". Es ist eine gespeicherte Kopie plus eine Regel dafür, wann diese Kopie noch gültig ist. Wenn du nur das Payload speicherst, aber die Regel weglässt, endest du mit zwei Realitäten: ein Bildschirm zeigt neue Infos, ein anderer die von gestern.

Ein praktisches Modell ist, jedes gecachte Element in einen von drei Zuständen zu setzen:

  • Fresh: sicher anzuzeigen.\n- Stale but usable: jetzt zeigen, im Hintergrund aktualisieren.\n- Must refresh: nicht vertrauen, vor Anzeige neu laden (oder einen Ladezustand zeigen).

Diese Einordnung macht die UI vorhersehbar, weil sie jedes Mal gleich reagieren kann, wenn sie einen Zustand sieht.

Frische-Regeln sollten auf erklärbaren Signalen basieren. Übliche Optionen sind zeitbasierte Abläufe (z. B. 5 Minuten), Versionsänderungen (Schema oder App-Version), Nutzeraktionen (Pull-to-Refresh, Submit, Delete) oder Server-Hinweise (ETag, last-updated-Timestamp oder eine explizite „cache invalid“-Antwort).

Beispiel: Ein Profilbildschirm lädt gecachte Nutzerdaten sofort. Ist es "stale-but-usable", zeigt er den gecachten Namen und Avatar und aktualisiert still im Hintergrund. Hat der Nutzer gerade sein Profil bearbeitet, ist das ein must-refresh-Moment. Die App sollte den Cache sofort aktualisieren, damit alle Bildschirme konsistent bleiben.

Entscheide, wer diese Regeln besitzt. Meistens ist die beste Default-Regel: die Datenebene (data layer) besitzt Frische und Invalidierung, die UI reagiert nur (zeige Cache, zeige Ladezustand, zeige Fehler) und das Backend gibt Hinweise, wenn möglich. Das verhindert, dass jeder Bildschirm seine eigenen Regeln erfindet.

Was cachen (und was nicht)

Gutes Caching beginnt mit einer Frage: Schadet es dem Nutzer, wenn diese Daten etwas veraltet sind? Ist die Antwort „wahrscheinlich nicht“, passt es meist für lokalen Cache.

Daten, die oft gelesen werden und sich langsam ändern, lohnen sich zu cachen: Feeds und Listen, über die Nutzer viel scrollen, Kataloginhalte (Produkte, Artikel, Templates) und Referenzdaten wie Kategorien oder Länder. Einstellungen und Präferenzen gehören ebenfalls dazu, ebenso grundlegende Profilinfos wie Name und Avatar-URL.

Risikohaft sind alles Geld- oder zeitkritische Daten. Kontostände, Zahlungsstatus, Verfügbarkeiten, Terminfenster, Liefer-ETAs und „zuletzt online“-Angaben können echte Probleme verursachen, wenn sie veraltet sind. Du kannst sie zur Beschleunigung cachen, musst aber den Cache vor Entscheidungszeitpunkten invalidieren (z. B. direkt vor Abschluss einer Bestellung).

Abgeleiteter UI-Zustand ist eine eigene Kategorie. Das Speichern des ausgewählten Tabs, Filter, Suchanfrage, Sortierung oder der Scroll-Position kann die Navigation glatt erscheinen lassen. Es irritiert aber auch, wenn alte Entscheidungen unerwartet wieder auftauchen. Eine einfache Regel: halte UI-Zustand im Speicher, solange der Nutzer in diesem Flow bleibt, und setze ihn zurück, wenn er bewusst "neu startet" (z. B. Rückkehr zum Home-Bildschirm).

Vermeide das Cachen von Dingen, die Sicherheits- oder Datenschutzrisiken schaffen: Geheimnisse (Passwörter, API-Keys), Einmal-Tokens (OTP, Passwort-Reset-Tokens) und sensible persönliche Daten, außer du brauchst wirklich Offline-Zugriff. Speichere niemals vollständige Kartendetails oder etwas, das das Betrugsrisiko erhöht.

In einer Shopping-App ist das Cachen der Produktliste ein großer Gewinn. Der Checkout-Bildschirm sollte jedoch immer Totals und Verfügbarkeit direkt vor dem Kauf bestätigen.

Auswahl der lokalen Cache-Ebene: Speicher, Disk, Datenbank

Die meisten Flutter-Apps brauchen einen lokalen Cache, damit Bildschirme schnell laden und nicht leer aufwachen, bis das Netzwerk antwortet. Die zentrale Entscheidung ist, wo gecachte Daten leben, denn jede Ebene hat unterschiedliche Geschwindigkeit, Größenlimits und Aufräumverhalten.

Ein Memory-Cache ist am schnellsten. Er ist ideal für Daten, die du gerade abgerufen hast und innerhalb der geöffneten App erneut verwenden wirst, wie das aktuelle Profil, die letzten Suchergebnisse oder ein gerade angesehenes Produkt. Der Nachteil: Er verschwindet, wenn die App beendet wird, hilft also nicht bei Cold-Starts oder Offline-Nutzung.

Disk Key-Value-Speicher eignet sich für kleine Items, die zwischen Neustarts erhalten bleiben sollen: Einstellungen, "zuletzt ausgewählter Tab" und kleine JSON-Antworten, die sich selten ändern. Halte es bewusst klein — große Listen in Key-Value zu lagern macht Updates kompliziert und lässt den Cache schnell aufblähen.

Eine lokale Datenbank ist die richtige Wahl, wenn Daten größer, strukturiert oder offline-fähig sein müssen. Sie hilft auch, wenn du Abfragen brauchst ("alle ungelesenen Nachrichten", "Warenkorb-Items", "Bestellungen des letzten Monats") statt eine große Blob-Datei zu laden und in Memory zu filtern.

Um Caching vorhersehbar zu halten, wähle für jeden Datentyp eine primäre Speicherstätte und vermeide, dass derselbe Datensatz an drei Orten liegt.

Kurzregeln:

  • Memory: Wiederverwendung innerhalb der aktuellen Session
  • Key-Value auf Disk: klein, einfach, selten ändernd
  • Datenbank: groß, strukturiert, offline-freundlich

Plane auch Größe und Aufräumen. Definiere, was „zu groß“ ist, wie lange du Einträge hältst und wie du bereinigst. Beispiel: Beschränke gecachte Suchergebnisse auf die letzten 20 Queries und entferne regelmäßig Einträge älter als 30 Tage, damit der Cache nicht unbemerkt wächst.

Aktualisierungsregeln für veraltete, aber nutzbare Daten

Refresh-Regeln sollten so einfach sein, dass du sie einem Kollegen in einem Satz erklären kannst. Dann zahlt sich vernünftiges Caching aus: Nutzer erleben schnelle Bildschirme, und die App bleibt vertrauenswürdig.

Die einfachste Regel ist TTL (time to live). Speicher Daten mit einem Zeitstempel und behandle sie für z. B. 5 Minuten als frisch. Danach sind sie veraltet. TTL passt gut zu "geschenkten" Daten wie Feeds, Kategorien oder Empfehlungen.

Eine nützliche Verfeinerung ist die Trennung von soft TTL und hard TTL.

Bei soft TTL zeigst du gecachte Daten sofort, dann aktualisierst du im Hintergrund und aktualisierst die UI, falls sich etwas geändert hat. Bei hard TTL hörst du auf, alte Daten nach Ablauf zu zeigen: du blockierst mit einem Loader oder zeigst einen "offline/try again"-Zustand. Hard TTL passt, wenn falsch zu liegen schlimmer ist als langsam zu sein (Kontostände, Bestellstatus, Berechtigungen).

Wenn dein Backend es unterstützt, bevorzuge "nur aktualisieren, wenn sich etwas geändert" mit ETag, updatedAt oder einem Versionsfeld. Die App kann fragen "hat sich etwas verändert?" und das vollständige Payload überspringen, wenn nichts neu ist.

Ein benutzerfreundlicher Default für viele Bildschirme ist stale-while-revalidate: sofort zeigen, still aktualisieren und nur neu zeichnen, wenn das Ergebnis abweicht. Das gibt Geschwindigkeit ohne zufälligen Flicker.

Typische Per-Screen-Frische-Regeln könnten so aussehen:

  • Home-Feed: soft TTL (1–5 Minuten), Hintergrund-Refresh beim Öffnen
  • Profil: soft TTL (15–60 Minuten), nach Bearbeitung refreshen
  • Nachrichten: hard TTL oder sehr kurze TTL (0–10 Sekunden), Refresh beim Resume
  • Checkout/Bestellung: hard TTL, immer Server bestätigen
  • Einstellungen/statische Listen: lange TTL (Tage), Refresh bei App-Update

Wähle Regeln basierend auf den Kosten, falsch zu liegen — nicht nur den Fetch-Kosten.

Wann den Cache invalidieren (wichtige Trigger)

Mit Credits und Empfehlungen sparen
Verdiene Credits, indem du teilst, was du gebaut hast, oder Teammitglieder und Freunde einlädst.

Invalidierung beginnt mit einer Frage: Welches Ereignis macht gecachte Daten weniger vertrauenswürdig als die Kosten des Neuladens? Wenn du eine kleine Menge von Triggern wählst und dich daran hältst, bleibt das Verhalten vorhersehbar und die UI fühlt sich stabil an.

Wichtige Trigger in echten Apps:

  • Nutzergetrieben: Pull-to-Refresh, Tippen auf Retry nach Fehler, oder Rückkehr zu einem Bildschirm, bei dem der Nutzer "aktuell" erwartet (z. B. Posteingang).
  • Lifecycle: App-Start (Cache wärmen, dann refreshen) und Resume aus dem Hintergrund (nur refreshen, wenn älter als TTL).
  • Identity: Login, Logout, Account-Wechsel. Behandle das als harte Grenze: lösche nutzerabhängige Caches und brich laufende Requests zum alten Nutzer ab.
  • Write-Aktionen (CRUD): nach Create/Update/Delete sofort den Cache aktualisieren, damit Bildschirme konsistent bleiben. Refetch nur, wenn der Server zusätzliche Logik anwendet (Sortierung, Berechtigungen, berechnete Felder).
  • Sicherheit: App-Upgrade, lokale Schema-Änderung, Feature-Flag-Änderungen. Erhöhe eine Cache-Version und wipe oder migriere.

Beispiel: Ein Nutzer ändert sein Profilfoto und geht zurück. Wenn du nur zeitbasiert aktualisierst, zeigt der vorherige Bildschirm möglicherweise weiter das alte Bild. Stattdessen behandel die Bearbeitung als Trigger: aktualisiere das gecachte Profilobjekt sofort und markiere es mit neuem Timestamp als frisch.

Halte Invalidierungsregeln klein und explizit. Wenn du das genaue Ereignis, das einen Cache-Eintrag invalidiert, nicht benennen kannst, wirst du entweder zu oft (langsam, springend) oder zu selten (veraltete Bildschirme) aktualisieren.

Schritt-für-Schritt: einen vorhersehbaren Caching-Flow implementieren

Beginne damit, deine wichtigsten Bildschirme aufzulisten und welche Daten jeder braucht. Denk nicht in Endpunkten, sondern in nutzersichtbaren Objekten: Profil, Warenkorb, Bestellliste, Katalogeintrag, ungelesene Anzahl.

Wähle dann eine einzige Quelle der Wahrheit pro Datentyp. In Flutter ist das meist ein Repository, das verbirgt, woher die Daten kommen (Memory, Disk, Netzwerk). Bildschirme sollten nicht entscheiden, wann das Netzwerk aufgerufen wird. Sie fragen das Repository und reagieren auf den zurückgegebenen Zustand.

Ein praktischer Flow:

  • Mappe Bildschirme auf benötigte Daten (und ob sie nutzergebunden oder geteilt sind).
  • Route alle Lese- und Schreibzugriffe über ein Repository pro Datentyp.
  • Speichere Daten mit Metadaten: savedAt Timestamp, Schema/App-Version und ownerUserId.
  • Definiere UI-Verhalten für vier Zustände: fresh, stale, loading, error.
  • Füge zwei Refresh-Pfade hinzu: manuell (Pull-to-Refresh) und Hintergrund (stiller Refresh, wenn stale).

Metadaten machen Regeln durchsetzbar. Wenn ownerUserId sich ändert (Logout/Login), kannst du alte gecachte Reihen sofort verwerfen oder ignorieren, statt kurz Daten des vorherigen Nutzers anzuzeigen.

Für das UI-Verhalten entscheide vorher, was "stale" bedeutet. Eine gängige Regel: zeige stale-Daten sofort, damit der Bildschirm nicht leer ist, starte einen Hintergrund-Refresh und aktualisiere bei neuen Daten. Scheitert der Refresh, behalte die stale-Daten und zeige einen kleinen, klaren Fehler.

Dann sperre die Regeln mit ein paar langweiligen Tests:

  • TTL: Daten werden nach X Minuten stale.
  • Logout: Cache für ownerUserId wird gelöscht oder isoliert.
  • Update: Lokale Schreiboperation aktualisiert Cache sofort, dann Sync.
  • Version bump: alter Cache wird nach Schema/App-Änderung ignoriert.
  • Fehler: Stale-Daten bleiben sichtbar, wenn Refresh fehlschlägt.

Das ist der Unterschied zwischen „wir haben Caching“ und „unsere App verhält sich jedes Mal gleich".

Bildschirme über Navigation hinweg konsistent halten

Deine Flutter-Codebasis besitzen
Behalte die volle Kontrolle, indem du den generierten Quellcode bei Bedarf exportierst.

Nichts zerstört Vertrauen schneller, als einen Wert in der Liste zu sehen, ins Detail zu tippen, ihn zu bearbeiten und beim Zurückkehren wieder den alten Wert zu sehen. Konsistenz kommt daher, dass jeder Bildschirm aus derselben Quelle liest.

Eine solide Regel lautet: einmal fetchen, einmal speichern, vielfach rendern. Bildschirme sollten nicht denselben Endpunkt unabhängig aufrufen und private Kopien behalten. Leg die gecachten Daten in einen gemeinsamen Store (dein State-Management) und lass sowohl Listen- als auch Detailansichten denselben Datenstand beobachten.

Eine Quelle der Wahrheit schaffen

Behalte einen zentralen Ort, der den aktuellen Wert und dessen Frische besitzt. Bildschirme können einen Refresh anstoßen, sollten aber nicht ihre eigenen Timer, Retries und Parser managen.

Praktische Gewohnheiten, die „zwei Realitäten“ verhindern:

  • Teile State über Routen hinweg (ein Store/Provider pro Feature, nicht pro Widget).
  • Dedupliziere laufende Requests, damit nicht zwei Bildschirme dasselbe parallel fetchen.
  • Bei Back-Navigation lies vom Store statt alten Widget-State wiederzubeleben.
  • Nach Bearbeitungen aktualisiere den Store sofort, damit alle Bildschirme die Änderung zeigen.

Sichtbar machen, was passiert

Auch mit guten Regeln sehen Nutzer manchmal stale Daten (offline, langsames Netz, backgrounded App). Mach das sichtbar mit kleinen, ruhigen Signalen: einem "Vor kurzem aktualisiert"-Zeitstempel, einem dezenten "Aktualisiere…"-Indikator oder einem "Offline"-Badge.

Bei Bearbeitungen fühlen sich optimistische Updates meist am besten an. Beispiel: Der Nutzer ändert im Detailbildschirm den Produktpreis. Aktualisiere den gemeinsamen Store sofort, damit die Liste beim Zurückkehren den neuen Preis zeigt. Schlägt das Speichern fehl, rolle zurück und zeige eine kurze Fehlermeldung.

Häufige Caching-Fehler und wie man sie vermeidet

Die meisten Caching-Ausfälle sind langweilig: Der Cache funktioniert, aber niemand kann erklären, wann er verwendet wird, wann er abläuft und wer ihn besitzt.

Die erste Falle ist Cachen ohne Metadaten. Wenn du nur das Payload speicherst, kannst du nicht sagen, ob es alt ist, welche App-Version es erzeugte oder welchem Nutzer es gehört. Speichere mindestens savedAt, eine einfache Versionsnummer und eine userId. Diese kleine Gewohnheit verhindert viele "Warum zeigt dieser Bildschirm falsche Daten?"-Bugs.

Ein weiteres Problem sind mehrere Caches für dieselben Daten ohne Besitzer. Eine Liste hält eine In-Memory-Liste, ein Repository schreibt auf die Disk und ein Detailbildschirm fetcht neu und speichert anderswo. Wähle eine Quelle der Wahrheit (oft die Repository-Ebene) und lasse jeden Bildschirm darüber lesen.

Account-Wechsel sind ein häufiger Stolperstein. Wenn sich jemand ausloggt oder das Konto wechselt, lösche nutzerbezogene Tabellen und Keys. Sonst siehst du kurz das Profilfoto oder die Bestellungen des vorherigen Nutzers, was wie ein Datenschutzproblem wirkt.

Praktische Fixes:

  • Speichere Cache-Einträge mit savedAt, version und userId, nicht nur JSON.
  • Definiere einen Owner pro Dataset (z. B. ProfileRepository besitzt Profile).
  • Verwende für die meisten Bildschirme die Regel „zeige Cache jetzt, aktualisiere im Hintergrund".
  • Rate-limite Refreshes (z. B. ein Netzwerk-Refresh pro X Minuten pro Endpoint).
  • Zeige Refresh-Fehler an (kleiner Banner oder Retry) und lege eine maximale Stale-Zeit fest.

Beispiel: Deine Produktliste lädt sofort aus dem Cache und aktualisiert leise. Scheitert der Refresh, zeige weiter gecachte Daten, mache aber klar, dass sie veraltet sein könnten, und biete Retry an. Blockiere die UI nicht, wenn gepufferte Daten völlig in Ordnung sind.

Kurze Checkliste vor dem Release

Bevor du veröffentlichst, verwandle Caching von "scheint in Ordnung" in Regeln, die du testen kannst. Nutzer sollten sinnvolle Daten sehen, selbst nach Hin- und Her-Navigation, Offline-Nutzung oder Login mit einem anderen Konto.

Frische- und Refresh-Verhalten definieren

Für jeden Bildschirm entscheide, wie lange Daten frisch bleiben können. Das können Minuten für schnell bewegliche Daten (Nachrichten, Kontostände) oder Stunden für langsam ändernde Daten (Einstellungen, Produktkategorien) sein. Bestimme dann, was passiert, wenn Daten nicht mehr frisch sind: Hintergrund-Refresh, Refresh beim Öffnen oder manuelles Pull-to-Refresh.

Invalidierung und Cache-Metadaten festlegen

Für jeden Datentyp entscheide, welche Ereignisse Cache leeren oder umgehen müssen. Gängige Trigger: Logout, Edit des Items, Account-Wechsel und App-Updates, die die Datenform ändern.

Stelle sicher, dass gecachte Einträge kleine Metadaten neben dem Payload speichern:

  • savedAt
  • userId
  • version (Schema/App-Version)
  • source (optional: network vs local)

Halte die Ownership klar: nutze ein Repository pro Datentyp (z. B. ProductsRepository), nicht pro Widget. Widgets fordern Daten an, sie entscheiden nicht über Cache-Regeln.

Definiere und teste offline-Verhalten: Was zeigen Bildschirme aus dem Cache, welche Aktionen sind deaktiviert, welcher Text erscheint ("Gespeicherte Daten anzeigen" plus sichtbare Refresh-Kontrolle). Manuelles Refresh sollte auf jedem cache-gestützten Bildschirm vorhanden und leicht zu finden sein.

Beispiel: Katalog-App, die konsistent bleibt

Account-Wechsel sauber handhaben
Lege nutzerspezifische Caching-Regeln an, um veraltete Daten nach Login/Logout oder Account-Wechsel zu vermeiden.

Stell dir eine einfache Shop-App mit drei Bildschirmen vor: Produktkatalog (Liste), Produktdetails und ein Favoriten-Tab. Nutzer scrollen, öffnen ein Produkt und tippen auf das Herz, um es zu favorisieren. Ziel ist, schnell zu wirken, auch bei schlechten Netzen, ohne verwirrende Inkonsistenzen.

Cachiere lokal, was dir hilft, sofort zu rendern: Katalogseiten (IDs, Titel, Preis, Thumbnail-URL, Favoriten-Flag), Produktdetails (Beschreibung, Specs, Verfügbarkeit, lastUpdated), Bildmetadaten (URLs, Größen, Cache-Keys) und die Favoriten des Nutzers (Set von Produkt-IDs, optional mit Timestamps).

Beim Öffnen des Katalogs zeige gecachte Ergebnisse sofort und revalidiere im Hintergrund. Kommen frische Daten, aktualisiere nur, was sich geändert hat, und bewahre die Scroll-Position.

Für den Favoriten-Toggle behandle ihn als "muss konsistent sein"-Aktion. Aktualisiere die lokale Favoritenmenge sofort (optimistisches Update) und aktualisiere gecachte Produktzeilen und Produktdetails für diese ID. Scheitert der Netzwerk-Call, rolle zurück und zeige eine kurze Nachricht.

Um Navigation konsistent zu halten, treibe Badges in der Liste und das Herz-Icon im Detail von derselben Quelle der Wahrheit (dein lokaler Cache/Store), nicht von getrenntem Screen-State. Die Liste aktualisiert sich sofort beim Zurückkehren, das Detail reflektiert Änderungen aus der Liste, und der Favoriten-Zähler stimmt überall, ohne auf einen Refetch zu warten.

Füge einfache Refresh-Regeln hinzu: Katalog-Cache läuft schnell ab (Minuten), Produktdetails etwas langsamer, Favoriten laufen nie ab, werden aber nach Login/Logout immer abgeglichen.

Nächste Schritte: Regeln dokumentieren und pflegen

Caching hört auf, mysteriös zu sein, wenn dein Team auf einer Seite mit Regeln verweisen kann und sich darauf einigt, was passieren soll. Ziel ist nicht Perfektion, sondern vorhersehbares Verhalten, das sich über Releases hinweg nicht ändert.

Schreibe eine kleine Tabelle pro Bildschirm und halte sie kurz genug, um sie bei Änderungen schnell zu prüfen: Bildschirmname und Hauptdaten, Cache-Ort und Key, Frische-Regel (TTL, ereignisbasiert oder manuell), Invalidierungs-Trigger und was der Nutzer beim Refresh sieht.

Füge leichtgewichtige Logs hinzu, während du feinabstimmst. Protokolliere Cache-Hits, -Misses und warum ein Refresh ausgelöst wurde (TTL abgelaufen, Nutzer hat gezogen, App resümiert, Mutation abgeschlossen). Wenn jemand berichtet: „Diese Liste wirkt falsch", machen solche Logs den Bug lösbar.

Beginne mit einfachen TTLs und verfeinere sie basierend auf Nutzerbeobachtungen. Ein News-Feed kann 5–10 Minuten Stale-Toleranz haben, ein Bestellstatus-Bildschirm sollte beim Resume und nach jedem Checkout eine Aktualisierung verlangen.

Wenn du schnell eine Flutter-App baust, hilft es, die Datenebene und Caching-Regeln vor der Implementierung zu skizzieren. Für Teams, die Koder.ai (koder.ai) nutzen, ist der Planning Mode ein praktischer Ort, um diese pro-Bildschirm Regeln zuerst zu definieren und dann passend umzusetzen.

Beim Abstimmen von Refresh-Verhalten schütze stabile Bildschirme während du experimentierst. Snapshots und Rollbacks sparen Zeit, wenn eine neue Regel versehentlich Flicker, leere Zustände oder inkonsistente Zähler über Navigation einführt.

FAQ

Was ist der einfachste Weg, Caching in einer Flutter-App vorhersehbar zu machen?

Beginne mit einer klaren Regel pro Bildschirm: Was darf sofort gezeigt werden (Cache), wann muss neu geladen werden und was sieht der Nutzer während der Aktualisierung. Wenn du die Regel nicht in einem Satz erklären kannst, wird die App irgendwann inkonsistent wirken.

Wie entscheide ich, ob gecachte Daten frisch, veraltet oder unbrauchbar sind?

Behandle gecachte Daten als Zustandsmaschine für Frische. Wenn sie fresh sind, zeige sie. Wenn sie stale but usable sind, zeige sie jetzt und aktualisiere still im Hintergrund. Wenn sie must refresh sind, lade neu bevor du zeigst (oder zeige einen Lade-/Offline-Zustand). So verhält sich die UI konsistent statt „manchmal aktualisiert, manchmal nicht“.

Welche Daten sollte ich cachen und welche nicht?

Cache Dinge, die oft gelesen werden und bei leichtem Veralten dem Nutzer nicht schaden: Feeds, Kataloge, Referenzdaten und grundlegende Profilinfos. Vorsicht bei geld- oder zeitkritischen Daten wie Kontoständen, Verfügbarkeiten, ETAs oder Bestellstatus: Du kannst sie cachen, musst aber vor einer Entscheidung immer frisch prüfen.

Soll mein Cache im Arbeitsspeicher, auf der Festplatte oder in einer lokalen Datenbank liegen?

Nutze Memory für schnellen Wiedergebrauch innerhalb der aktuellen Session (aktuelles Profil, zuletzt angeschautes). Nutze Key-Value auf Disk für kleine, einfache Items, die Restarts überdauern sollen (Einstellungen). Nutze eine lokale Datenbank, wenn Daten groß, strukturiert, abfragbar oder offline-brauchbar sein sollen (Nachrichten, Bestellungen, Inventar).

Reicht eine TTL (time-to-live) Regel für die meisten Bildschirme aus?

Eine einfache TTL ist ein guter Ausgangspunkt: Daten gelten für eine bestimmte Zeit als frisch, dann wird neu geladen. Für viele Bildschirme ist jedoch „zeige Cache jetzt, aktualisiere im Hintergrund und zeichne nur bei Änderung neu“ besser, weil es leere Bildschirme vermeidet und Flicker reduziert.

Welche Ereignisse sollten eine Cache-Invalidierung auslösen?

Invalidiere bei Ereignissen, die das Vertrauen in den Cache sichtbar senken: Nutzeränderungen (create/update/delete), Login/Logout oder Kontowechsel, App-Resume falls älter als TTL, und explizites Nutzer-Refresh. Halte die Trigger klein und explizit, damit du nicht ständig oder nie aktualisierst.

Wie verhindere ich, dass die Listenansicht alte Daten anzeigt, nachdem ich Details bearbeitet und zurückgekehrt bin?

Lass beide Bildschirme aus derselben Quelle lesen, nicht aus privaten Kopien. Wenn der Nutzer im Detail etwas ändert, aktualisiere das gemeinsame Cache-Objekt sofort, damit die Liste beim Zurückkehren den neuen Wert zeigt; synchronisiere dann mit dem Server und mache bei Fehlern ein Rollback.

Wie verhindere ich, dass gecachte Daten nach Logout oder Account-Wechsel zwischen Nutzern durchsickern?

Speichere neben dem Payload immer Metadaten, besonders einen Zeitstempel und eine Nutzerkennung. Bei Logout oder Account-Wechsel lösche oder isolier nutzergebundene Cache-Einträge sofort und breche laufende Requests für den alten Nutzer ab, damit nicht kurzzeitig die Daten des Vorbesitzers angezeigt werden.

Was sollte die UI tun, wenn eine Hintergrund-Aktualisierung fehlschlägt?

Standardmäßig: Gecachte Daten weiter anzeigen und einen kleinen, klaren Fehlerzustand mit Retry anbieten, statt den Bildschirm leer zu machen. Wenn der Bildschirm alte Daten nicht sicher zeigen kann, dann verwende eine must-refresh-Regel und zeige ein Lade- oder Offline-Messaging, anstatt veraltete Werte vorzutäuschen.

Wo sollte die Cache-Logik liegen: Widgets, State Management oder Datenebene?

Lege die Cache-Regeln in der Datenebene (z. B. Repositories) ab, damit alle Bildschirme dasselbe Verhalten teilen. Schreibe zuerst die pro-Bildschirm Frische- und Invalidierungsregeln (z. B. in Koder.ai Planning Mode), und implementiere dann so, dass die UI lediglich auf Zustände reagiert statt eigene Refresh-Logik zu erfinden.

Related posts