Open-source project
theachoem/storypad avatar
theachoem/storypad

StoryPad: a timeline journal built on Flutter with a local-first store

An open source diary & journal app with over 100k downloads 🪴

980 stars129 forksDartGPL-3.0

At a glance

What is it?
StoryPad is an open source journal app for Android, iOS, macOS and Linux that stores entries in a local ObjectBox database and organises them as a timeline rather than folders. It suits people who want a private diary on their own device with export to Markdown, and it stops at the boundary where cross-device sync beyond Google Drive is needed.
Who is it for?
Adopt StoryPad if you want a private journal that lives on your own device, if a timeline suits how you think, and if you are content with Google Drive as the sync mechanism. Do not adopt it if you need encrypted server-side sync, collaborative entries, or a store listing that has to stay closed source, because the licence is GPL-3.0 and the README states that modifications must remain open source under the same terms.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly Dart, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A journal organised as a timeline, not as folders

StoryPad is a diary application whose central organisational idea is that there are no folders or tabs. The README describes timeline journaling where your life flows naturally, and the entry list is a single chronological stream you scroll through. Alongside that sit the features you would expect from a journal that people actually keep: throwback memories that show what you wrote on the same day in previous years, photos attached to a page with a custom layout, a feelings and moods tracker covering more than 45 emotions with history and calendar views, multi-page entries for longer writing, and tags, stars and search to find something again.

The writing surface is customisable in ways that go past plain text. Bold, lists, checkboxes and colours are available, and more than 1300 Google Fonts are offered. There are more than 20 colour themes with dark and light mode, and the interface ships in more than 20 languages.

The audience is people who want a private, local journal and care about the feel of the app rather than about a feature checklist for teams. The description calls it privacy-first, minimalist and open source, and the app is published for both Android and iOS, with the project site at storypad.me and a community on r/storypad.

Three tiers of state, and a view split into four files

The codebase organisation is documented in the README and is unusual enough to be worth reading before you contribute. State is kept in three levels so that lifetimes do not overlap. Global state is managed by a ProviderScope in lib/provider_scope.dart and disposed when the app closes. View state is managed by a ChangeNotifierProvider from the provider package and disposed when the page closes. Widget state is handled by Stateful widgets, where the widget owns its state and disposes of it when it is removed from the tree.

On top of that sits an MVVM arrangement. Each view is composed of three or four files: a Model that represents the data structure and is optional, for example StoryDbModel; a View that constructs the view model and builds the UI content, for example EditStoryView; a ViewContent that displays the actual UI, keeping visual layout apart from business logic; and a ViewModel that holds the business logic and supplies data and operations to the view, keeping the UI free of unnecessary logic.

For a Flutter project this is a deliberate choice rather than a default. The usual Flutter guidance is a single setState-heavy widget, and splitting every screen into four files with a separate ViewContent component adds a lot of files to read before you can change anything. The payoff is that the business logic and the visual layer can be edited and reasoned about separately, and the project says as much, describing the aim as code that stays understandable while following Flutter's principles.

ObjectBox on device, and what privacy-first means in practice

Persistence is handled by ObjectBox, which the README describes as the local database solution for persistent data storage, chosen for fast operations and rich search capabilities suited to mobile data handling. There is no server component in the picture. The repository layout backs that up: there is a lib/ directory for the application code, a packages/ directory that suggests shared local packages, and platform folders for android/, ios/, macos/ and linux/.

The privacy claim follows from that architecture rather than from a marketing sentence. The feature list states that a PIN, FaceID and fingerprint lock protect the app, and that data stays on your device. Backup and sync are described as a private Google Drive sync plus an offline local export, which means the sync path goes through a consumer file service rather than a first-party server.

That is a reasonable trade for a personal diary, and it has consequences. The user controls the export, so a diary written over years is not trapped in the app, and export options include text, markdown and full backups with attachments such as images and audio. But the sync story is one vendor deep, so if your Drive account is compromised the diary is exposed, and if you want a backup that is not tied to one account you have to do the export yourself.

Running the app from source on each platform

The setup section lists the toolchain first: Java 21 LTS for Android, Ruby 3.3.5 for iOS, and Flutter 3.29.0. A .tool-versions file sits at the repository root alongside pubspec.yaml and pubspec.lock, and the build is driven by build.yaml.

The project ships a helper script that picks the right target, and the README gives one command per platform:

sh
# For android
bin/dev --community

# For ios
bin/dev --community-ios

# For macos
bin/dev --community-macos

The community flag matters. The GoogleService-Info.plist and google-services.json files are provided directly in the repository, and bin/dev generates an empty Dart-defines fallback automatically, so the paid integrations, named in the README as RevenueCat, Maps and Google Sign-In, simply do nothing. That is how you get a build without provisioning accounts for third-party services, and it is why the script names the community variant rather than the full one.

A local story is built from a directory of plain-text files, and the command line takes a path override for that directory. The tests live in test/, the shared assets in assets/, and the interface translations in translations/.

What the GPL-3.0 licence means for the paid tier

StoryPad is licensed under the GNU General Public License v3.0, and the README is unusually direct about why. The stated reason is to ensure the source code remains free and open: anyone can use, study, modify and distribute it, but modifications must also remain open source under the same licence.

This interacts with the StoryPad Pro tier, which is a one-time purchase and adds backgrounds, custom daily templates, relaxing sounds, a period calendar, a voice journal, Markdown export, writing statistics, pinned notes and automatic Google Drive backup. The repository is GPL, so anyone who modifies the app and distributes the modified build has to publish that source under the same terms. Reading the licence file is the way to know exactly what that covers for a given distribution, and this is not legal advice.

The other side of the licensing is what you get as a user. The core application, including the timeline, the moods tracker with 45 or more emotions, photos, biometric locks, tags, search and the 20-plus themes, is open source and free, and the feature list separates the Pro additions from the core set. The two store badges in the README point at the Play Store and the App Store listings, so the question of what the paid version unlocks is worth checking against the listing before you build a habit on it.

Where StoryPad stops being the right tool

The boundaries are mostly about scope, and they are predictable.

There is no server. Everything is local plus Google Drive, so there is no shared or multi-device-instant journal, no web client, and no way to read your entries from a browser on a machine where the app is not installed. If your diary needs to be openable anywhere, the export is the only path and it is manual.

Second, the timeline is the organising principle, and it is a single one. There are tags and stars, but no folders, no custom hierarchies and no nested structure. People whose notes accumulate as projects or research areas will feel that constraint, because the app gives no way to build a parallel view over the same entries.

Third, the writing feature set is text and images, with voice notes listed as a Pro feature and audio carried in full backups. Rich media beyond that is out of scope.

Fourth, the local database is a single device store, and the README does not document any encryption-at-rest story beyond the app lock, nor a mechanism for migrating a diary between devices other than the backup export and the Drive sync. Fifth, the project targets current Flutter and current platform tooling, so building it means keeping Java 21, Ruby 3.3.5 and Flutter 3.29.0 around.

Against a folder of Markdown files

The alternative worth considering is not another journal app, it is a directory of text files in a folder that you open in any editor, synced by whatever file sync you already use. For many people that setup is enough and it has real advantages: the data is yours in a format that needs no application to read, it diffs and backs up cleanly, and it survives the app being discontinued.

The difference in approach is what you trade. A plain folder gives you the storage and nothing else. There is no timeline, no throwback view, no mood tracking, no biometric lock on the app itself, no formatting palette with more than 1300 fonts, and no built-in export because the files are already the export. Reading your own diary from a phone is a matter of opening a text editor.

StoryPad is the opposite trade. You give up the file-level ownership and get a designed reading and writing experience: a chronological stream with memories surfaced by date, moods with a calendar view, photos laid out per page, and a lock screen in front of it. For someone who writes daily and wants the ritual of a journal app, that experience is the product. For someone who writes rarely and mainly wants to keep text safe and portable, the folder is enough, and StoryPad would be adding an install and a store dependency to solve a problem you do not have.

There is also a middle path the project itself supports: the Pro Markdown export is stated to be fully compatible with Obsidian, Notion and other editors, so a diary can start inside the app and end up as plain files.

Editorial conclusion

Adopt StoryPad if you want a private journal that lives on your own device, if a timeline suits how you think, and if you are content with Google Drive as the sync mechanism. Do not adopt it if you need encrypted server-side sync, collaborative entries, or a store listing that has to stay closed source, because the licence is GPL-3.0 and the README states that modifications must remain open source under the same terms. Verify first on your own platform that the community build starts, since the setup path differs per target and requires Java 21, Ruby 3.3.5 and Flutter 3.29.0. Releases move regularly, with 2.33.1 on 2026-09-13 and the last push on 2026-10-19.

Frequently asked questions

What does StoryPad use as its local database?

ObjectBox, described in the README as the local database solution for persistent data storage, chosen for fast operations and rich search capabilities. There is no server-side component, which is what allows the app to state that data stays on your device.

How do I run StoryPad from source?

Install Java 21 for Android, Ruby 3.3.5 for iOS and Flutter 3.29.0, then use the helper script for your platform: bin/dev --community for Android, bin/dev --community-ios for iOS or bin/dev --community-macos for macOS. The community variants generate an empty Dart-defines fallback so RevenueCat, Maps and Google Sign-In do nothing.

Does StoryPad sync my diary between devices?

Backup and sync are described as a private Google Drive sync plus an offline local export, and automatic Google Drive sync is listed as a StoryPad Pro feature. There is no first-party sync server, so the sync path runs through Google Drive.

How does StoryPad organise entries, can I use folders?

The README describes timeline journaling with no folders or tabs, so entries flow in one chronological stream. Organisation comes from tags, stars and search, and there is a multi-page entry format for longer writing.

What licence is StoryPad released under?

GNU General Public License v3.0, with the LICENSE file at the repository root. The README states the reason is to keep the source free and open, and that any modifications must also remain open source under the same licence.

Can I get my StoryPad entries out as Markdown?

Yes, Markdown export is a StoryPad Pro feature and the README describes it as fully compatible with Obsidian, Notion and other editors. Standard exports also include text and full backups with attachments such as images and audio.

Official sources

  1. License: GPL-3.0
  2. Project website
  3. README
  4. Releases
  5. theachoem/storypad on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/theachoem-storypad.svg)](https://hysenlabs.com/projects/theachoem-storypad)

Was this useful?