Building an Offline-First Flutter Application
An offline-first application assumes that the network may be unavailable.
Instead of designing:
UI -> Network -> Data
design the application around local data:
┌──────────────┐
│ Flutter │
└──────┬───────┘
|
Repository
/ \
v v
Local Database API
^ |
| |
+----Sync-+
The UI should remain useful even when the network is unavailable.
Why Offline-First?
Benefits include:
- better user experience
- faster screen loading
- resilience to network failures
- reduced API usage
- support for intermittent connectivity
Choose a Local Database
Common options include:
- SQLite-based solutions
- Drift
- Isar
- Hive
- SharedPreferences for simple preferences
For structured application data, choose a database rather than storing large datasets in preferences.
Repository as the Source of Truth
The UI should normally communicate with a repository:
abstract class ProductRepository {
Stream<List<Product>> watchProducts();
Future<void> refreshProducts();
}
The repository can combine local and remote sources.
Read From Local Data First
A common pattern is:
Open screen
↓
Read local database
↓
Display cached data immediately
↓
Refresh from API
↓
Store new data locally
↓
UI receives updated data
This gives users fast feedback.
Sync Flow
A simple synchronization strategy:
Local data
↓
Check connectivity
↓
Upload pending changes
↓
Download server changes
↓
Resolve conflicts
↓
Update local database
Do not make the UI responsible for synchronization.
Pending Operations
When the user performs an action offline, store the operation locally.
Example:
class PendingOperation {
final String id;
final String type;
final Map<String, dynamic> payload;
PendingOperation({
required this.id,
required this.type,
required this.payload,
});
}
The operation can later be synchronized.
Example Queue
Offline:
Create order #1001
Update profile
Add note
Local queue:
[Create order]
[Update profile]
[Add note]
Network returns:
↓
Process queue
↓
Server
Conflict Resolution
Conflicts happen when local and server data change independently.
For example:
Device A:
name = "John"
Device B:
name = "Jonathan"
Both update while offline.
Possible strategies include:
- last-write-wins
- server-wins
- client-wins
- field-level merging
- manual conflict resolution
Choose a strategy based on the business domain.
Connectivity Is Not the Same as Internet Access
A device may report that it is connected to Wi-Fi while the internet is unavailable.
Therefore, an application should not assume:
Wi-Fi connected = API reachable
The API request itself is the strongest evidence that the server is reachable.
Optimistic UI
For some actions, update the UI immediately:
User taps Like
↓
UI changes immediately
↓
Save locally
↓
Send API request
If the server rejects the operation, reconcile the local state.
Retry Strategy
Do not retry failed requests continuously.
Use:
Attempt 1
↓
Wait
↓
Attempt 2
↓
Longer wait
↓
Attempt 3
Exponential backoff reduces unnecessary network traffic.
Architecture
A production offline-first feature can use:
UI
|
BLoC
|
Use Case
|
Repository
| v v
Local DB Remote API
|
Sync Engine
The repository coordinates data access while the synchronization service manages consistency.
Testing Offline Behavior
Test at least:
- first launch without internet
- cached data display
- creating data offline
- editing data offline
- reconnecting
- failed synchronization
- duplicate requests
- conflict resolution
Common Mistakes
Making every screen call the API directly
This makes offline support extremely difficult.
Treating connectivity state as truth
Connectivity indicators are useful hints, not proof that the backend is reachable.
No synchronization state
Users should be able to understand whether data is:
Synced
Pending
Syncing
Failed
Conclusion
Offline-first architecture changes the role of the local database. It is no longer just a cache; it becomes a central part of the application's data flow.
When implemented correctly, users can continue working even when connectivity is unreliable.
Useful Links
SDK Flutter: https://github.com/v-modal/vmodal_sdk_flutter
SDK Android: https://github.com/v-modal/vmodal_sdk_android
Discord: https://discord.gg/K72z28KUx
Top comments (0)