Flutter : Building Offline-First Apps with Supabase
Because real users don’t always have five-bar 5G… and your app shouldn’t taste bland when the internet disappears.
1. Why Care About Offline-First? 🌶️
Think of a food-delivery app that loses your order the moment the elevator hits a dead zone—annoying, right? Offline-first design records every action locally first and syncs when the network wakes up, giving your UX the resilience of well-slow-cooked biryani.
2. The Ingredient List 🍲
Spice | Purpose |
---|---|
Flutter 3.22+ | The sauté pan where everything simmers |
supabase_flutter | Auth, real-time DB, REST interface |
Brick 📦 | Local persistence + queued sync layer |
Connectivity Plus | Detects when to trigger sync |
Workmanager | Background tasks on Android/iOS |
Riverpod / Bloc | State-management masala—pick your flavour |
3. Setting Up Supabase (10 min prep)
-
Create a project at https://supabase.com → copy anon & service keys.
-
Schema – example
notes
tableSQLcreate table public.notes ( id uuid primary key default uuid_generate_v4(), title text not null, body text, updated_at timestamp with time zone default now() );
-
Row-level security
SQLalter table public.notes enable row level security; create policy "Users can access own notes" on notes for all using ( auth.uid() = user_id );
4. Bootstrapping Flutter & Brick 🛠️
SHELLflutter create offline_supabase_demo cd offline_supabase_demo flutter pub add supabase_flutter brick_offline_first sqflite connectivity_plus workmanager riverpod
4.1 Brick Repository
DARTimport 'package:brick_offline_first/brick_offline_first.dart'; class Note extends OfflineFirstWithRestModel { @primaryKey final Uuid id; final String title; final String? body; @OfflineFirstSerdes() final DateTime updatedAt; Note({required this.id, required this.title, this.body, required this.updatedAt}); }
Run code‑gen:
SHELLflutter pub run build_runner build --delete-conflicting-outputs
Brick now gives you:
DARTfinal repository = OfflineFirstWithRestRepository( restProvider: RestProvider( 'https://<YOUR-PROJECT>.supabase.co/rest/v1', headers: {'apikey': supabaseAnonKey}, ), offlineProvider: SqliteProvider.databaseBuilder('notes.db').build(), );
5. The Sync Flow 🔄
MERMAIDgraph LR USER_ACTION[User edits note] -->|Repository.save()| LOCAL_DB LOCAL_DB -->|enqueue| QUEUE[Brick Sync Queue] QUEUE -->|onConnectivity| SUPABASE SUPABASE -->|webhooks| LOCAL_DB
- Create/update ⇒ saved instantly in SQLite.
- Sync queue persists mutation list.
Connectivity().onConnectivityChanged
triggersrepository.synchronize()
automatically.- Supabase “UPDATE” triggers real-time listeners → UI refresh.
6. Conflict Resolution (Sweet vs Spicy) 🌗
Strategy | When to use |
---|---|
Last‑write‑wins (default) | Personal data, low risk |
Timestamp merge | Collaborative docs |
Manual diff UI | High‑stakes records (financial, medical) |
Brick exposes hooks:
DARTrepository.addBeforeSaveTrigger((existing, incoming) { if (incoming.updatedAt.isAfter(existing.updatedAt)) return incoming; return existing; // keep older change });
7. Background Sync with Workmanager ⏰
DARTvoid callbackDispatcher() { Workmanager().executeTask((task, inputData) async { await Supabase.initialize(...); await repository.synchronize(); return Future.value(true); }); } void main() { WidgetsFlutterBinding.ensureInitialized(); Workmanager().initialize(callbackDispatcher, isInDebugMode: false); Workmanager().registerPeriodicTask('sync-task', 'offlineSync', frequency: const Duration(minutes: 15)); runApp(const MyApp()); }
8. Taste‑Test: Simulating Offline 🧪
- Android Emulator → Cellular → No Connectivity.
- iOS Simulator → Features → Network Link Conditioner.
- Keep DevTools open; Brick logs queued mutations so you can verify they replay when connectivity returns.
9. Security & Cost Tips 💰
- Enable PostgREST rate limits on Supabase to avoid abuse.
- Use Supabase Edge Functions for heavy joins—ship only what mobile truly needs.
- Purge the sync queue after 90 days with a cron job to keep SQLite lean.
10. Conclusion — Serve Hot, Even Offline 🔥
With Supabase as the back‑of‑house kitchen and Brick as your local tiffin box, a Flutter app can stay flavour‑packed whether users are on a bullet train or stuck in an elevator. Clone the demo, sprinkle your own masala (animations, theming), and tweet your build—let’s make flaky connections a non‑issue.
Further Reading
- Brick Docs – offline‑first patterns
- Supabase Flutter Guide – auth & RLS
- My FlutterAhmedabad Talk Replay – 25‑min crash course
Feeling hungry for more? Drop a comment below or ping me on the Contact page—happy to taste‑test your prototypes!