Skip to content
←Projects
8 June 20262 min readIn development

Kairos - a Flutter app rebuilt to work offline.

I rebuilt a productivity app in Flutter so it works offline: from depending on a FastAPI server to storing data on the device and syncing it with Supabase.

FlutterBlocRealmSupabaseGemini
Kairos - productivity app
What it is
A productivity app in Flutter: tasks, focus mode, habits and stats.
My role
Personal project: both versions, from architecture to tests.
Stack
Flutter · Bloc · Realm · Supabase · Gemini
Result
Works offline and syncs when the network returns · the Gemini key never lives on the device · logic covered by tests.
Status
In development.
Links
View code ↗ (opens in a new tab)

Kairos started as a class project with one idea: treating time as something you owe. If you left a task half done, it added minutes of "productive debt" and Gemini wrote a stoic reflection. It has had two versions, and the interesting part is why I moved from one to the other.

The first version depended on the server

It was a Flutter app with Provider that talked to a FastAPI server. The server stored tasks and debt, and one endpoint, "the confessional", asked Gemini for the reflection. It also had geofencing, to tie tasks to a place. It worked, but offline it was useless, and an app for organising your day has to open without signal.

The rewrite: data on the phone first

In the second version, tasks are stored on the phone with Realm, so they can be created and completed offline. Each one carries a sync flag, and when there's a session and a network, pending ones go up to Supabase.

Supabase replaced FastAPI: PostgreSQL with Row Level Security, so each user can only read and write their own data without coding that check into every request. My own server no longer added anything.

The Gemini key never goes on the phone

The AI is called from Supabase Edge Functions. One takes the tasks and returns them sorted by priority; if Gemini fails, it returns them as they were, so the app is never left waiting.

Organised by feature, testable without the interface

Tasks, focus mode, habits and stats, each with data, logic and screen kept apart. Bloc handles state and GetIt the dependencies, which lets me test the logic, such as the task and focus-mode Blocs, without starting the interface.

What was left behind

Geofencing and time debt. The database already has the debt table and the reflection function, but the app doesn't use them yet: I preferred a solid offline base to dragging along features from the old server.

What I learned: architecture gets paid for later

Putting the server at the centre was the easy choice at first; putting the phone at the centre is what makes the app useful day to day. Rebuilding it was costly, but it taught me to judge every decision by what it costs later.

Built byIsmael Manzano LeónFull Stack Developer · leo/ · leosoftware.dev
View on GitHub→ (opens in a new tab)

Does this fit what you are looking for? Write to me →