DriversRecommendedOutdated drivers can make a good PC feel brokenScan driver issues before chasing fixes manually.Scan NowOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run Scan×
Skip to content
World desk4 min

How I Built a Local-First Journaling App with Flutter, Drift and Firebase

Mindleau saves journal entries to SQLite before attempting optional Firebase sync. Here’s how its repository boundary, pending state, conflict rule, anonymous auth, and migrations fit together.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

I built Mindleau so someone can start journaling without an account or internet connection: each entry is saved to the device first, and optional cloud sync runs separately in the background. In this implementation, Flutter provides the iOS and Android app, Drift and SQLite hold the local source of truth, and Firebase Auth and Cloud Firestore support sync when available. This is one app’s architecture and set of tradeoffs, not a universal recipe or a guarantee of security.

Why local-first shaped the architecture

Mindleau is a free iOS and Android brain-dump journal and mood tracker. I wanted its basic use to work before sign-up and without connectivity, rather than making a cloud account the gate to writing. That makes the local database more than a cache: the app can read and save entries there whether or not sync is configured or reachable.

As an Amazon Associate I earn from qualifying purchases.

The stack is Flutter, Drift’s typed SQLite interface, Firebase Authentication, and Cloud Firestore. Provider with ChangeNotifier and ValueNotifier handles app state. These choices describe this implementation; the article does not report package versions, benchmarks, or an independent security assessment.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Keep the UI away from Firestore

The central rule in my design is: “The UI never talks to Firestore.” The UI reads and writes through a repository. That repository persists data in SQLite, while a separate sync service handles communication with Firestore. This boundary keeps a network failure from becoming a prerequisite for saving an entry.

When a user saves an entry, the app inserts it locally, marks it as pending, and starts a sync attempt without making the UI wait for that attempt. The entry is immediately available from SQLite; if sync fails, the local copy remains and the pending work can be retried when connectivity returns.

Represent pending edits and deletions

For syncable rows, I store a remote document ID, an updated timestamp, a deleted timestamp, sync status, and last-sync timestamp. Together, these fields let the app associate a local row with its remote document, identify work that still needs to be sent, and record when a row was last synchronized.

Deletion needs explicit representation. Rather than removing a synced row outright, the app records a deletion timestamp—a tombstone—so that deletion intent can travel to another device. Without that signal, a later pull could make an item reappear because the remote copy would have no indication it was deleted locally.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

This implementation keeps sync metadata on each row. Another design is a separate outbox table that records operations to deliver. The article describes the per-row approach but does not compare the two with measurements; the relevant choice depends on how much operation history and retry control the app needs.

Pull first, then push local work

The sync service follows a simple sequence: pull remote changes, then push local preferences and pending records. For records that changed on both devices, it uses updatedAt as a last-write-wins rule. The version with the later timestamp takes precedence.

That rule is simple, but it can discard an edit: if the same entry is changed offline on two devices, one version will win instead of merging both changes. I considered that tradeoff acceptable for this personal-use app. It is my judgment about this product, not a generally safe conflict policy. An approach that preserves or merges concurrent edits, such as a CRDT, can avoid that specific loss mode but adds complexity.

Let people begin without an account

Local journaling does not require Firebase authentication. When sync begins, the app signs in anonymously and uses that identity for cloud sync. The author’s account path allows an email-link credential to be linked later to the same UID, so account-free onboarding can coexist with a stable cloud identity.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

If anonymous authentication is disabled or unreachable, the app skips sync and continues using local SQLite. This keeps the account and network dependency on the sync path rather than the writing path.

Bound network waits and keep failures off the save path

Network calls use a 15-second timeout in this implementation to avoid waiting indefinitely on an unreliable connection. Sync errors are logged. Because the UI does not await sync, those errors do not undo a local save; pending work can be retried when connectivity returns. The timeout is the author’s implementation detail, not a measured guarantee of reliability.

Plan Drift migrations for existing installs

Adding sync to a shipped local database means changing a schema that already contains user data. I versioned the Drift schema and added upgrade steps for sync columns and a table. One migration lesson was that a new NOT NULL column added to an existing SQLite table needs a database-level default if old rows must satisfy the constraint.

Drift’s Dart-side clientDefault is not the same as a SQL default: it supplies a value in Dart and does not provide the database-side default required by a raw ALTER TABLE migration. When evolving a local schema, account for how existing rows are populated, not only how newly inserted rows receive values.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Sync only data that earns its way into the cloud

I keep guided stillness sessions on the device. They contribute to streaks and a 30-day heatmap, but I judged their cross-device value too small to justify additional sync complexity and sending that data off-device. That is a product decision rather than a rule that this kind of data should always remain local.

Selective sync makes the tradeoff explicit: syncing a table can improve continuity across devices, while local-only storage avoids that transfer and its associated implementation work. Decide data by data whether cross-device access is worth the cost.

What this design does—and does not—establish

The implementation demonstrates one way to make local saves independent of cloud availability: SQLite is the source of truth, the repository separates UI from storage and sync, and background synchronization carries pending changes when possible. It also has clear limits: timestamp-based conflict resolution can lose simultaneous offline edits, and successful account-free use does not itself establish anything about encryption, privacy protections, or security quality.

The account is Ayesha Iftikhar’s first-person description of Mindleau, published on DEV Community on September 23 and edited September 26; the year is inferred from the source’s publication label as 2025. Read the original article.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Crashes, No Sound, or Screen Glitches?Free driver scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.