← Flutter Reference

What is Offline-First in Flutter?

Data & Connectivity · Advanced

By Abdelrahman Saed — Senior Mobile Engineer. Last updated: 2026-08-10.

Offline-first is a mobile architecture pattern where the local database is the primary source of truth for the UI. Reads resolve instantly from the device, writes are persisted locally and replayed on reconnect, and the network's role is background reconciliation — not blocking the user interface.

How it works

In an offline-first app, every read goes to the local database first. The UI never waits on a network round-trip for data it already has. A background sync engine reconciles the local database with the server.

User opens app → reads from local DB (instant) → background sync updates DB
User writes → persisted locally (instant) → queued and replayed on reconnect

The data flow at iStoria (5M+ users) uses PowerSync for streaming sync and Drift for the typed local SQLite database:

class StoriesRepository {
  StoriesRepository(this._remote, this._local);
  final StoriesRemoteDataSource _remote;
  final StoriesLocalDataSource _local;  // reads from Drift

  Future<Either<Failure, List<Story>>> fetchStories() async {
    try {
      final stories = await _local.cachedStories();    // instant local read
      unawaited(_remote.refreshInBackground());         // sync, never blocks UI
      return Right(stories);
    } on CacheException catch (e) {
      return Left(CacheFailure(e.message));
    }
  }
}

The key insight: the repository returns local data immediately and kicks off a background refresh it does NOT await. The UI gets instant data; PowerSync converges the local DB with the server asynchronously; the UI picks up changes reactively through Drift's stream.

Offline writes use a queue — a Dio interceptor backed by a local Hive box captures writes made while offline, returns an optimistic success so the UI keeps moving, and replays them automatically on reconnect:

When to use it

Use offline-first when:

  • Users are on unreliable or metered connections (mobile, travel, rural areas).
  • The app's worst moment would be a spinner waiting for the user's own data.
  • You need writes to survive going offline (completing a lesson on the subway).

Don't use offline-first when:

  • The app is inherently real-time (a live dashboard, a trading app) where stale data is worse than no data.
  • Your data set is too large to cache locally and too sparse to selectively sync.
  • The sync/conflict-resolution logic would be more complex than the app itself.

Strengths

  • Instant reads — no spinner on the user's own data, ever
  • Writes survive network drops — no lost work, no retry UX
  • Bandwidth savings — the app doesn't re-fetch what it already has
  • Better UX on slow connections — the app feels fast regardless of network quality
  • Every feature built afterward inherits offline support for free

Tradeoffs

  • Higher architecture complexity — local database, sync engine, conflict resolution, write queue
  • Conflict resolution is hard — what happens when two devices write to the same record?
  • Storage management — the local database grows, and old data must be pruned
  • Security — queued offline writes must strip auth tokens before persisting to disk

Alternatives

  • Local-First — Local-first architecture makes the device's local database the authoritative source of truth. The server syncs in the background, but never blocks reads or writes.

Comparisons

Related articles

Related case studies

FAQ

What is the difference between offline-first and local-first?

Offline-first means the app works without a network — reads are cached, writes are queued. Local-first goes further: the local database IS the source of truth, and the server's role is to sync, not to serve. Every local-first app is offline-first, but not every offline-first app is local-first. See the offline-first vs local-first comparison for details.

What database should I use for offline-first Flutter?

Drift (typed SQLite with code generation) is the most popular choice for structured data. For key-value storage, Hive or Isar. For sync, PowerSync (which syncs Drift tables with a Postgres backend) or Supabase Realtime. At iStoria, we use PowerSync + Drift.


Available for hire. Abdelrahman Saed is a Senior Mobile Engineer (Flutter) — open to full-time, fractional, contract, or advisory work. Hire me →

Book a 20-minute call · Download the CV (PDF) · See how I work