Local Data Persistence
Data persistence is an important requirement for many different apps.
To understand why this is important, consider an app where users can track their weight daily. It would be bad if the user entered some data, and this was lost after the app was backgrounded or killed by the operating system.
By saving this data locally to disk, you can retrieve it the next time the app is started and present it again.
In Flutter, there are various ways to do this. 👇
Using a Key-Value Store
You can use shared_preferences to store non-sensitive user data. This may include things like app preferences and settings (such as light/dark mode) or even a boolean flag to verify if the user has completed the app onboarding.
Another popular key-value store is Hive, which is used and loved by many developers. Hive is cross-platform, has a simple and intuitive API, is very fast, and has strong encryption built-in.
Using a Secure Key-Value Store
Sensitive data such as the user’s email and address should not be saved in plain text (unencrypted).
When dealing with sensitive data, it’s best to use a secure key-value store such as flutter_secure_storage. This has an API that is very similar to shared preferences but uses the underlying Keychain on iOS and KeyStore on Android.
Using a Relational Database (SQL)
If you need to store relational data, you can use sqflite, the SQLite plugin for Flutter.
This provides some asynchronous APIs for reading from and writing to a local database using the SQL language.
However, sqflite is not type-safe, and this can lead to bugs that are hard to diagnose.
To solve this problem, the Drift package was created. Drift is a type-safe reactive persistence library for Flutter and Dart, built on top of SQLite. Drift has extensive documentation covering everything you need to know.
Here’s a complete list of all the top SQL packages:
Using a NoSQL database
Thanks to SQL, relational databases are very powerful and a robust choice for data persistence.
But they also have a bit of a learning curve. And if you want to start with something simpler, consider NoSQL alternatives such as Isar, ObjectBox, or Realm.
Here’s a complete list of all the top NoSQL packages:
Reading and Writing Files to Disk
Key-value stores and relational databases are great, but sometimes you just want to write data directly to files on disk.
This is done by combining the path_provider plugin with the dart:io library.
Use case: the image_picker plugin offers a Future-based API that you can use to take a picture using the native camera. When you use this, the picture is saved on disk and returned as a
File
object that you can read withdart:io
or show inside a widget.
Which package to use?
If you’re finding it hard to choose from the options above, I recommend this:
- shared_preferences for non sensitive data like app preferences and settings.
- flutter_secure_storage for sensitive data including email addresses, API access tokens, etc.
- drift if you need to store complex relational data and want both type-safety and the power of SQL at your fingertips.
- path_provider and
dart:io
for saving local files to disk.
If you’re trying to decide where to store your API keys, read: How to Store API Keys in Flutter: —dart-define vs .env files
Local vs Remote Data Persistence
So far, we have only talked about local data persistence.
This works fine if you don’t need to sync data across multiple devices. With local persistence, users will lose their data if they move to a new device without backing up.
Here’s a good tutorial about local data persistence by the Flutter team:
Local data persistence is good, but many apps need access to user-generated content that is saved remotely in the cloud.
In this case, you’ll need to create your own backend or use a Backend-as-a-Service such as Firebase, Supabase, or AppWrite.
In particular, Firebase offers things like authentication, a real-time persistent document store, cloud functions, and many other useful features. In fact, there is so much to learn that I’ve created a complete course about Flutter & Firebase:
Daily Challenge - Weight Tracker
Implement a simple weight tracker app, which works as follows:
- The user can enter his/her weight into a form
- When the form is submitted, the weight is saved to Shared Preferences using a predefined key
- When the app is killed and restarted, it will show the previous weight (or empty if it was never used before)
At any time, the user can submit a new weight, replacing the previously saved value.
Feel free to design the UI however you like, as long as it satisfies the requirements above.
Happy coding!