Flutter & Dart

Building Offline-First Mobile Apps with Flutter and Dart

Two personal mobile projects that keep working with no connection at all.

· Full Stack Developer · Updated:

Angular covers the web side of my work; on mobile, my pick is Flutter. Flutter is Google's UI framework for shipping Android and iOS apps from a single codebase, and Dart is the language it runs on. It draws the interface with its own rendering engine rather than the platform's native widgets, so a screen looks the same on every device. During development hot reload shows a change within seconds, while release builds compile Dart ahead of time to native machine code.

I've used Flutter on two personal mobile projects. Bütçem is an income, expense and budget tracker that runs fully offline, with no server and no account; all of its data lives in a single SQLite file on the phone. Harfsayı is a Turkish word and number game you can play live against friends; a .NET 10 and SignalR server sits behind it, yet it stays playable without a connection. Both use Riverpod for state management and Drift for the local database.

Working offline forces you to model data correctly on the device itself. Bütçem stores amounts as integers in minor units (kuruş) and records every movement as double-entry postings, because a balance kept in floating-point numbers drifts by a cent sooner or later. I didn't skip testing either: Bütçem has 794 tests covering pure services, database migrations and end-to-end screen flows, including per-screen query budget checks and a performance test that runs against 10,000 records.

When a user's data lives only on their own phone, its correctness rests entirely on your code.

Drift lets you declare tables as Dart classes and generates type-safe query code ahead of time with build_runner; watch a query and every insert into the underlying table pushes a fresh result down the stream. Bind that stream to a Riverpod provider and only the widgets that depend on that data rebuild when it changes. Totals like the running balance are computed by the database in a single SQL query rather than by looping over a list in Dart, which is what keeps things fast as the record count grows.

Flutter's single-codebase promise has limits. Bütçem's home-screen widget and the install step its self-update needs aren't solvable inside Flutter; both required native Kotlin code. Distributing a signed APK instead of going through a store also makes updates your problem, which is why the app updates itself from a version.json file. Offline play has its own cost in synchronization: in Harfsayı, results played without a connection must never be written to the server twice, so the sync is idempotent, keyed by the device's UUID.