Offline-first architecture in Flutter: the Benim Sınavım case
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:
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:
- Answer records are append-only; there is no such thing as a conflict.
- Statistics are derived from answers; they're recalculated after sync.
- For the few genuine conflict candidates like user settings, the rule is simple: last write wins, with timestamps from the server clock, not the device clock.
Offline-first isn't a feature, it's a contract: the UI never learns about the network.
Things to watch out for
- Design queue tasks to be idempotent; processing the same task twice must not change the result.
- Show sync status quietly (a small icon is enough); the "did my data survive?" anxiety destroys trust.
- Download and store the content bundle during onboarding; requiring internet on first launch breaks the offline promise from the start.
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.
Product manager & mobile developer. Writes about product, performance and good interfaces.