Skip to content

Case study

Sortive

A productivity and personal finance app I designed, built and published to Google Play. Fully usable offline, and synced across devices when back online.

Overview

Sortive is a productivity and personal finance app I designed, built and published to Google Play. It combines task management, an hourly day planner, rich-text notes and expense tracking, with a dashboard pulling the four together.

The problem

I wanted one app covering tasks, planning, notes and money, that worked with no connection and still synced across devices. Most apps do one of those two things well and treat the other as an afterthought.

What I built

Task management with categories and status, an hourly day planner with sub-tasks, a rich-text notes system, expense tracking with income settings and categories, and a dashboard pulling all four together. Local-first storage in Isar so the app is fully usable offline, with Firebase Authentication and Cloud Firestore providing sync across devices and per-user data isolation.

Built in Flutter as a monorepo: the application plus separate shared packages for core logic and the UI system, with Riverpod for state and go_router for navigation.

Complexities tackled

Two-way sync is where offline-first projects die. Offline-first inverts the usual direction: the local database is the source of truth and the cloud reconciles to it, which is the opposite of what most SDK examples assume. Getting bidirectional sync right, so that edits made on two devices while both were offline merge sensibly rather than one silently overwriting the other, took several passes and is the single hardest thing in the app.

Local schema changes are permanent. With a cloud database you migrate the server. With an on-device database, every user is running whatever version they last installed, and a careless schema change means data loss on someone’s phone. Local models have to be treated as a published contract.

Splitting the packages before I needed to. Core logic and the UI system were extracted into their own packages early, which felt like overhead at three screens and is the reason features stayed quick to add at thirty.

Four apps in a trench coat. Tasks, planner, notes and expenses each want a different data shape and a different interaction model, and the dashboard has to make them feel like one product rather than four tabs.

Reminders have to fire in the user’s timezone, not the server’s. A planner that notifies at the wrong hour is worse than one that does not notify at all. Notifications are scheduled locally against the device’s own timezone database rather than as pushes from a server, so they still arrive with no connection, and they still arrive at the right local hour after the user has flown somewhere.

A finance app has to lock. Expense tracking with income settings is exactly the kind of thing a person does not want visible when they hand their unlocked phone to someone. The app gates behind the device’s own biometric or passcode check, using the platform’s authentication rather than a password of its own, because a password I invent is a password I then have to store.

Per-user isolation is enforced by the database, not by the client. Sync means every device is writing to shared infrastructure, so the rules that say a user can only read their own documents live in the database’s own rule set. Client-side filtering is a display choice; it is not access control.

It runs on the desktop too. Window management is wired in, so the same codebase produces a desktop build rather than only a phone one. Most of the cost of that is in the layout work, which is why it was worth doing while there were still few screens.

Stack

Flutter and Dart, Isar for local storage, Firebase Authentication with Google Sign-In, Cloud Firestore with server-side security rules, Riverpod, go_router. A Melos-managed monorepo splitting the app from a core package and a UI package. Rich text through flutter_quill, timezone-aware local notifications, device biometric authentication, image picking and cropping, and desktop window management.

Screens

Working on something similar?

Tell me what you are building and what is in the way. I will tell you honestly whether I am the right person for it.

Start a conversation

Last updated: