Strategies for local data

Hi all, rookie new guy here.

I’m working on a meal planning app, and one of the features I want is for it to be usable offline and to have user account creation be optional. So my thought is to ship the app with ~200 recipes stored locally as a JSON. The user would need to be able to write to it as well.

I’m new to both Flutter and app dev, so I’m wondering how the idea strikes you? Any pitfalls and/or better options?

Store it as an asset. If on app launch you don’t find any user database, create it from the data in the asset.

1 Like

What would you say is the tradeoff between storing it in assets vs using a plugin like path_provider?

Read and write files | Flutter

The point is that assets get bundled with the app so it’s easy to distribute and then to initialize your local database on the Filesystem from the asset because you can’t write back to assets.

path_provider just gives you the locations of the platform Filesystem where you normally store your data.
We currently store everything in folders leveraging the Filesystem as hierarchical database you could say with json files because we don’t need any queries so I didn’t want to add any database packages.

If you need more complex queries sqlite or ObjectBox would be my favorites

My take after few years of trial and error is following. In most of the use-cases the SQLite database (or several if you want to separate it) checks all the boxes:

  • it’s super common in programming so it’s easy to find references how to use it and build stuff
  • you can export it as a file to your computer and debug easily with a myriad of free tools (e.g. https://sqlitebrowser.org/)
  • there are amazing Dart/Flutter packages, my favorite is drift, but there are also other great choices like floor
  • it’s easy to make it encrypted with SQLCipher
  • you can access it from native code if ever needed (e.g. home screen widget, foreground service)
  • you can sync it with the backend - either manually or using some 3rd party tools (powersync etc.), I do it manually and have some models mirrored between backend and local database
  • you can listen to queries via Stream subscriptions, meaning your UI can just update anytime the underlying data changes
  • you can store binary data if needed
  • objects can maintain relations so changes are reflected between them
  • you can access the same database from multiple isolates to have non-blocking access to data
  • the maintainers are nice folks and tend to help and answer issues quite quickly

There are some challenges too:

  • you have to implement and maintain migrations, we use some step-by-step migration helpers, and after almost 2 years we’re at schema version 30+; we will probably get rid of some migrations and just create a new database at some point
  • you have to create mappers for custom data types (e.g. uuid, json, lists)
  • you cannot write to the same database from multiple places at the same time, you either have to think about locking mechanism, queuing or falling back to database WAL mode/busy_timeout etc.
  • sometimes errors are quite cryptic if they come from the native low-level realm

So if you intend to use database, imho spend some time learning about SQLite. After that most of the solutions like objectbox, realm, or sembast will be easier to grasp, and you will get a good comparison reference. They all have shiny features, but imho most of them is not that needed in day-to-day work.

6 Likes

This is good information, thanks all! Sounds like SQLite is on the homework list :slight_smile:

I would suggest SQLite with drift. It will load files faster than the filesystem, can flexible query needs, has beeb around longer than flutter has existed, and also super fast and scales with your application.

You can even store the json directly in SQLite in binary as JSONB or as text:
https://www.sqlite.org/json1.html

I even wrote a blog post on how to use SQLite as a NoSQL store:

3 Likes

Just had a nice discussion on that on @escamoteur.bsky.social on Bluesky

1 Like

I mostly use shared preferences for settings and an encrypted sqlite prepopulated database.

I have bad memories working with JSONB and would not recommend it if you plan on doing queries with the data contained in the JSON especially if there’s multiple layers of nesting :sweat_smile: