← All posts

Offline-first architecture in Flutter: the Benim Sınavım case

Mar 14, 20262 min readFlutterSupabase

Benim Sınavım users pick strange places to solve questions: school buses, the metro, villages, dorm canteens. What these places share is internet that's either slow or absent. So I set a rule on day one: the app works completely without internet; connectivity is a bonus used for sync.

Local first, cloud second

The core of the architecture is simple: the single source of truth is the on-device database. The UI never waits for the network; it always reads from and writes to local storage. The network layer is a separate world running in the background.

Writes flow through a queue:

sync_queue.dart
Future<void> saveAnswer(Answer answer) async {
  // 1) Write to the local database immediately; the UI sees this
  await db.answers.put(answer);
 
  // 2) Add a sync task to the queue
  await db.syncQueue.put(SyncTask(
    type: SyncType.answer,
    payload: answer.toJson(),
    createdAt: DateTime.now(),
  ));
 
  // 3) Try to flush if there's a connection; otherwise later
  unawaited(syncService.flush());
}

When connectivity returns, flush() sends the queue to Supabase in order. The user notices none of this; they solve a question and see their stats instantly.

Conflicts: less scary than they sound

The first fear in multi-device sync is conflicts. In practice, choosing the right data model shrinks the problem:

Offline-first isn't a feature, it's a contract: the UI never learns about the network.

Things to watch out for

The bonus of this architecture came from an unexpected place: the app got faster even with a connection, because no screen ever waits on network latency.

Murat Özdemir

Product manager & mobile developer. Writes about product, performance and good interfaces.