Offline-First Flutter: How We Built a CRM That Manages 100K+ Leads With No Internet The article describes how Aqarmap, Egypt's largest property platform, built an offline-first CRM for real estate agents managing over 100,000 leads monthly. The architecture ensures the app remains fully functional without internet by having the UI interact exclusively with a local SQLite database, with a sync engine queuing all writes and processing them in order once connectivity is restored. Key features include transactional writes to prevent sync failures, a single listener to avoid overlapping syncs, and version-based conflict resolution that merges field-level changes rather than blindly overwriting data. Most apps quietly assume the network is always there. Then a real user walks into a basement, a half-built apartment tower, or an elevator — and the app falls apart. https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcp8075nhij4celrfr649.png For a real-estate sales agent, that moment isn't a glitch. It's a lost lead, and a lost commission. When we built the AM Live CRM at Aqarmap Egypt's largest property platform , our field agents were managing 100,000+ leads a month — and a huge chunk of their day happened exactly in those dead zones: new developments, underground parking, remote plots with one bar of signal. A "cache the last response" approach wasn't enough. We needed the app to be fully usable with zero connectivity — create a lead, move it through the pipeline, log a call — and have all of it sync cleanly the moment the network came back. Here's the architecture we landed on, and the mistakes worth avoiding. The core idea: the local database is the source of truth The biggest mental shift in offline-first is this: the UI never talks to the network directly. It talks to the local database. The network is just a background process that keeps the local store and the server eventually consistent. php UI / BLoC - Repository - Local DB SQLite <- Sync engine <- API Every read comes from SQLite. Every write goes to SQLite first, then gets queued for the server. The user never waits on a request, and never sees a spinner that depends on signal. Pillar 1 — Write locally, queue the intent When an agent edits a lead, we do two things in one transaction: update the local row, and record the intent to sync it. class SyncOperation { final String id; // uuid final String entity; // 'lead', 'call log', ... final String entityId; final OpType type; // create | update | delete final Map