Flutter Interview Handbook

Offline-First Synchronization

100%

Offline-First Synchronization

Building resilient mobile applications requires an Offline-First Architecture. Instead of assuming constant network connectivity, an offline-first app treats the local database as the primary source of truth, queuing background write mutations and resolving state conflicts when connectivity is restored.


1. Offline-First Architectural Pipeline

 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”              1. Optimistic Write              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚   User UI     β”‚ ────────────────────────────────────────────► β”‚  Local Database   β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜                                               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚                                                                 β”‚
         β”‚ 2. Read Local State Immediately                                 β”‚ 3. Queue Action
         β–Ό                                                                 β–Ό
 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”              4. Sync Mutation                 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚ State Manager β”‚ ◄──────────────────────────────────────────── β”‚  Mutation Outbox  β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                                           β”‚
                                                               5. Network Restored
                                                                           β–Ό
                                                                 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                                                 β”‚   Remote Server   β”‚
                                                                 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The 4 Core Pillars

  1. Local Primary Source of Truth: UI widgets subscribe exclusively to local database queries (via Streams or ValueListenables).
  2. Optimistic UI Updates: State changes are written locally and rendered on screen instantly without waiting for network ACK responses.
  3. Persistent Mutation Outbox: Pending write operations (POST, PUT, DELETE) are serialized into a persistent SQLite/Hive table to survive app kills.
  4. Background Sync Worker: Automatically drains the outbox queue when internet connectivity transitions from offline to online.

2. Persistent Mutation Outbox Pattern

To prevent data loss when a user performs mutations offline and force-closes the app, the outbox queue must be persisted to disk:

class PendingMutation {
  final String id;
  final String action; // e.g. 'UPDATE_USER_PROFILE'
  final Map<String, dynamic> payload;
  final DateTime createdAt;
  final int retryCount;

  PendingMutation({
    required this.id,
    required this.action,
    required this.payload,
    required this.createdAt,
    this.retryCount = 0,
  });

  Map<String, dynamic> toMap() => {
    'id': id,
    'action': action,
    'payload': jsonEncode(payload),
    'createdAt': createdAt.toIso8601String(),
    'retryCount': retryCount,
  };
}

Exponential Backoff with Jitter

When retrying failed outbox mutations, calculate delays using exponential backoff with randomized jitter to prevent server stampedes:

Delay = 2^(retryCount) * 1000ms + RandomJitter(0..500ms)

3. Conflict Resolution Strategies

When multiple clients modify the same record offline, synchronization can produce data conflicts.

1. Last-Write-Wins (LWW)

  • Mechanism: Overwrites server data using the client timestamp (updated_at).
  • Limitation: Vulnerable to client clock skew! If a user’s phone clock is set 5 minutes into the future, their updates overwrite newer server data indefinitely.

2. Conflict-free Replicated Data Types (CRDTs)

  • Mechanism: Data structures mathematically designed to merge concurrent updates without central coordination or conflict errors.
  • Types:
    • LWW-Element-Set: Sets with timestamp-based additions and removals.
    • PN-Counter: Counters that track independent increments and decrements.

3. Server-Authoritative Merge

  • Mechanism: The server compares incoming payload version numbers (version_id). If mismatched, the server rejects the write, computes a merged field-level payload, and returns the merged object for client reconciliation.

4. Trade-offs & Production Considerations

  • Optimistic UI Rollbacks: If an optimistic update fails permanently on the server (e.g. 403 Forbidden or validation error), the client app must roll back local database changes and notify the user gracefully.
  • Outbox Queue Bloat: If an app remains offline for weeks, the outbox queue can grow large. Cap max outbox entries and implement compaction policies to discard superseded duplicate operations.