effidevFlutter · Cloudflare-Edge · Cloud-Kostenoptimierung
Deutsch

Flutter Supabase Offline-First: Drift & PowerSync Synchronisation

Flutter Supabase Offline-First Architektur mit Drift und PowerSync

Wenn eine mobile App in der U-Bahn, im Aufzug oder in Funklöchern mit instabiler Netzwerkverbindung einfriert und nur einen „Netzwerkfehler“-Spinner anzeigt, löschen Benutzer die App sofort. Die herkömmliche Architektur „Netzwerkanfrage ➔ Lesen aus dem Cache bei Fehlschlag“ kann häufige Ladespinner und das Anzeigen veralteter Daten (Stale State) nicht vermeiden.

Der weltweite Standard für die Entwicklung mobiler Apps im Jahr 2026 ist die Offline-First (Local-First)-Architektur. In einer Offline-First-App dient die lokale SQLite-Datenbank auf dem Gerät oder im Browser als Single Source of Truth (einzige Quelle der Wahrheit), und die Benutzeroberfläche reagiert sofort, ohne den Netzwerkstatus überhaupt zu kennen.

Dieser Artikel zeigt die vollständige Implementierung einer Enterprise-Offline-First-Architektur in einer Flutter- und Supabase (PostgreSQL)-Umgebung. Durch die Kombination von Drift SQLite, der PowerSync-Synchronisationsengine und Riverpod 3.0 wird 100 % Offline-Reaktionsfähigkeit und Echtzeit-Serversynchronisation garantiert.

Zusammenfassung

  • Single Source of Truth: Die Benutzeroberfläche ruft die Supabase-API nicht direkt auf, sondern abonniert ausschließlich die lokale Drift SQLite-Datenbank. Bei Datenänderungen wird eine Reaktionszeit von 0ms garantiert.
  • Rolle der PowerSync-Engine: Automatisiert partielle Synchronisation (Partial Sync), Konfliktlösung (Conflict Resolution) und asynchrones Outbox-Handling zwischen lokaler SQLite und Supabase Postgres vollständig.
  • Riverpod 3.0-Integration: Durch das Abonnieren des gebundenen Streams der lokalen SQLite über streamProvider wird die UI selbst im Offline-Zustand sofort aktualisiert.
  • Netzwerktransparenz: Selbst wenn das WLAN in der U-Bahn unterbrochen wird, können Benutzer weiterhin Daten schreiben und bearbeiten. Bei der Wiederherstellung der Verbindung wird automatisch im Hintergrund mit dem Backend synchronisiert.
  • Abkehr von veralteten Architekturen (wie Brick): Anstelle von einfachem HTTP-Caching oder übermäßigen Wrapper-Bibliotheken verhindert die Kombination aus Drift + PowerSync als Standard von 2026 Rendering-Jank und Datenverlust zu 100 %.

1. Das Offline-First-Paradigma: Netzwerk-Caching vs. Local-First

Dies ist der entscheidende Unterschied zwischen herkömmlichen Caching-Methoden und der Offline-First-Architektur von 2026.

[Herkömmliche Netzwerk-Caching-Methode] ❌
UI ──► Network Fetch (300ms Spinner) ──(bei Fehlschlag)──► Lokalen Cache lesen (veraltete Daten)

[2026 Offline-First-Architektur] ⭕️
UI ──(0ms sofortiges Lesen/Schreiben)──► [Drift Local SQLite (Single Source of Truth)]

                                           │ (Hintergrund-Sync 0ms~bei Netzwerkverbindung)

                                   [PowerSync Engine]

                                           │ (Echtzeit-Sync)

                                  [Supabase PostgreSQL]

In einer Offline-First-Struktur gibt es keinerlei Einschränkungen für den Betrieb der App, selbst wenn die Netzwerkverbindung getrennt ist. Alle Lese- und Schreibvorgänge für Daten werden sofort in der Drift SQLite-Datenbank auf dem Gerät ausgeführt, worauf die Benutzeroberfläche in 0ms reagiert. Die PowerSync-Engine zeichnet Änderungen in einer Hintergrund-Outbox-Warteschlange (Outbox Queue) auf und synchronisiert sie sofort mit Supabase, sobald die Netzwerkverbindung wiederhergestellt ist.

2. Drift SQLite + PowerSync + Riverpod 3.0 Architektur-Layer

Schicht Rolle & Komponenten Hauptmerkmale
UI & State Layer Flutter UI + Riverpod 3.0 (NotifierProvider) Abonniert unabhängig vom Netzwerkstatus nur den Stream der lokalen Drift DB
Local Persistence Drift (SQLite ORM) Single Source of Truth. Typsichere Dart SQLite-Datenbank
Sync Engine PowerSync Client Echtzeit-Differenzsynchronisation & Konfliktlösung zwischen lokaler SQLite und Supabase Postgres
Remote Database Supabase (PostgreSQL) RLS (Row Level Security)-basierter Mandanten-Datenspeicher

3. Praktische Offline-First-Implementierung

Schritt 1: Drift SQLite-Tabellendefinition (lib/database/app_database.dart)

Entspricht der offiziellen Drift-Dokumentation für eine typsichere lokale Datenbankdefinition.

import 'package:drift/drift.dart';
import 'package:drift/native.dart';

part 'app_database.g.dart';

class Todos extends Table {
  TextColumn get id => text()();
  TextColumn get title => text()();
  BoolColumn get isCompleted => boolean().withDefault(const Constant(false))();
  DateTimeColumn get updatedAt => dateTime()();

  @override
  Set<Column> get primaryKey => {id};
}

@DriftDatabase(tables: [Todos])
class AppDatabase extends _$AppDatabase {
  AppDatabase() : super(NativeDatabase.memory());

  @override
  int get schemaVersion => 1;

  // Lokale DB-Änderungen als 0ms Stream bereitstellen
  Stream<List<Todo>> watchAllTodos() {
    return (select(todos)..orderBy([(t) => OrderingTerm.desc(t.updatedAt)])).watch();
  }
}

Schritt 2: PowerSync Offline-Schema & Konfliktlösungs-Setup (lib/sync/powersync.dart)

Konfiguration des Supabase Offline-Hintergrundsynchronisations-Connectors über die offizielle PowerSync-Dokumentation.

import 'package:powersync/powersync.dart';

final schema = Schema([
  Table('todos', [
    Column.text('title'),
    Column.integer('is_completed'),
    Column.text('updated_at'),
  ])
]);

late final PowerSyncDatabase db;

Future<void> initPowerSync(String supabaseUrl, String anonKey) async {
  db = PowerSyncDatabase(schema: schema, path: 'app_powersync.db');
  await db.initialize();

  // Supabase-Connector verbinden (führt Hintergrundsynchronisation aus, wenn das Netzwerk verbunden ist)
  final connector = SupabaseConnector(url: supabaseUrl, anonKey: anonKey);
  await db.connect(connector: connector);
}

Schritt 3: Riverpod 3.0 Offline-reaktives UI-Abonnement (lib/providers/todo_provider.dart)

Kombiniert das in unserem Riverpod 3.0 Architektur-Leitfaden behandelte AsyncNotifier- und Stream-reaktive Muster, sodass die UI unabhängig von der Netzwerkverbindung sofort reagiert.

import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../database/app_database.dart';

final databaseProvider = Provider<AppDatabase>((ref) => AppDatabase());

// Riverpod 3.0 StreamProvider, der den Stream der lokalen Drift DB in Echtzeit abonniert
final todoListProvider = StreamProvider.autoDispose<List<Todo>>((ref) {
  final db = ref.watch(databaseProvider);
  return db.watchAllTodos();
});

// Beim Hinzufügen von Todos in der UI: Sofortiges Einfügen in die lokale DB ohne Netzwerkanfrage
class TodoNotifier extends AutoDisposeAsyncNotifier<void> {
  @override
  Future<void> build() async {}

  Future<void> addTodo(String title) async {
    final db = ref.read(databaseProvider);
    await db.into(db.todos).insert(
      TodosCompanion.insert(
        id: DateTime.now().millisecondsSinceEpoch.toString(),
        title: title,
        updatedAt: DateTime.now(),
      ),
    );
  }
}

4. Praxistestmuster für Netzwerktrennung und Wiederherstellung

Um die Validität einer Offline-First-App zu beweisen, müssen Sie einen Netzwerkmanipulationstest (Network Manipulation Test) durchführen.

[Test-Szenario]
1. Flugmodus (Offline) aktivieren ➔ 5 neue Notizen in der App erstellen ➔ 0ms sofortige Bildschirmentsprechung bestätigen ⭕️
2. App beenden und neu starten (Offline-Status) ➔ 100 % lokale Gültigkeit der erstellten Daten bestätigen ⭕️
3. WLAN-Verbindung wiederherstellen ➔ PowerSync synchronisiert im Hintergrund automatisch mit Supabase Postgres ⭕️
4. Supabase-Dashboard ➔ RLS-Richtlinien angewendet & fehlerfreie Übertragung ohne Datenkonflikte bestätigen ⭕️

Durch die Anwendung dieser Architektur tritt der in unserem Flutter DevTools Leitfaden für Speicherlecks und CPU-Profiling hervorgehobene UI-Thread-Rendering-Jank nicht auf, und die Bildrate von 60–120 fps bleibt perfekt erhalten.

5. Leitfaden zur Auswahl von Offline-First-Bibliotheken 2026

Bibliothek Offline-First-Eignung Bewertung & Empfehlungsgrund
PowerSync + Drift ★★★★★ (Empfohlen) Best Practice für 2026. Perfekte Harmonie aus Partial Sync, Konfliktlösung und Typsicherheit von Drift
Drift + Custom Outbox ★★★★☆ (Benutzerdefiniert) Hervorragend, wenn Sie Sync-Warteschlangen und REST/GraphQL-Connectors ohne Drittanbieter-Dienste selbst erstellen
Supabase Realtime Cache ★★☆☆☆ (Nicht empfohlen) Kein Offline-Schreiben möglich. Nur auf Echtzeit-Abonnements bei aktiver Netzwerkverbindung spezialisiert
Brick ORM ★☆☆☆☆ (Nicht empfohlen) Fehler bei der serverseitigen Löschsynchronisation und komplexer Boilerplate-Code machen die Wartung 2026 unattraktiv

Häufig gestellte Fragen (FAQ)

Entstehen keine Konflikte, wenn mehrere Geräte dieselben Daten im Offline-Zustand bearbeiten?

Die PowerSync-Engine löst Konflikte im Hintergrund automatisch über LWW (Last-Write-Wins)-Zeitstempelregeln oder CRDT (Conflict-free Replicated Data Type)-Algorithmen. Wenn spezielle Geschäftslogik erforderlich ist, können Sie diese einfach steuern, indem Sie Supabase Postgres-Triggerfunktionen oder benutzerdefinierte PowerSync-Konflikthandler definieren.

Was passiert, wenn die lokale SQLite DB-Kapazität zu groß wird?

Mithilfe der partiellen Synchronisationsregeln (Partial Sync Rules) von PowerSync können Sie nur die Daten selektiv auf das lokale Gerät herunterladen, die der Benutzer derzeit benötigt (z. B. Daten der letzten 30 Tage oder eigene Daten), wodurch der Arbeitsspeicher und der Speicherplatz des Geräts minimiert werden.

Ist die Sicherheit der Offline-Daten in Supabase gewährleistet?

Wenn Sie die sqlcipher-Verschlüsselungsengine auf die lokale Drift SQLite-Datenbankdatei anwenden, kann die Datenbankdatei selbst in Jailbreak- oder Rooting-Umgebungen auf dem Gerät nicht entschlüsselt werden. Darüber hinaus führt die serverseitige RLS (Row Level Security)-Richtlinie von Supabase bei der Wiederherstellung der Verbindung eine zweite Überprüfung durch, sodass die Sicherheit strikt aufrechterhalten wird.

Funktioniert Offline-First auf der Web-Plattform (Flutter Web) genauso?

Ja, das funktioniert. In der Flutter Web-Umgebung wechselt Drift automatisch zum Wasm + IndexedDB-Backend und erstellt eine Offline-SQLite-Datenbank im Browser. Die Wasm-Rendering-Performance in Web-Umgebungen wurde in unserem Flutter Web Wasm + Skwasm Performance-Leitfaden ausführlich behandelt.