Data Persistence on Flutter
See how to persist data to storage in a Flutter app, including to files and to a remote datastore, and use a Repository interface for the persistence. By JB Lorenzo.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Data Persistence on Flutter
30 mins
- Getting Started
- What Kind of Persistence Can You Use
- Serialized Data and Encryption
- Saving Data to Memory
- Flutter Persistence Options
- Persisting Data in a Key-Value Store
- Saving Plain Text with Key-Value Store
- Reading Plain Text with Key-Value Store
- Swapping out the Persistence Layer
- Removing Plain Text with Key-Value Store
- Saving Image with Key-Value Store
- Saving Objects into a Key-Value Store
- Persisting Data in Disk with Files
- Saving Your Data to Files
- Getting Strings, Images, and Objects from Files
- Persisting Data Online
- Saving Your Data Online
- Reading Your Data Online
- Removing Your Data Online
- Where to Go From Here?
Saving Your Data Online
Start by opening lib/data/CloudPersistence.dart. Then, add imports for dart:convert
,dart:convert
and for the firebase_storage
package:
import 'dart:convert';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_storage/firebase_storage.dart';
Next, update the save methods to the following:
@override
Future<String> saveImage(String userId, String key, Uint8List image) async {
// 1
final StorageReference storageReference =
FirebaseStorage().ref().child('$userId/$key');
// 2
final StorageUploadTask uploadTask = storageReference.putData(image);
// 3
await uploadTask.onComplete;
return '$userId/$key';
}
@override
void saveObject(
String userId, String key, Map<String, dynamic> object) async {
// 4
await Firestore.instance
.collection('users')
.document(userId)
.collection('objects')
.document(key)
.setData(object);
}
@override
void saveString(String userId, String key, String value) async {
// 5
await Firestore.instance
.collection('users')
.document(userId)
.collection('strings')
.document(key)
.setData({'string': value});
}
Here’s what you did:
- When saving an image, you get a reference to Firebase storage where you will save the image. The path is at
userId/key
. - Then, you call
putData
on the storage reference. - Next, you wait for the upload to finish. Finally return the path used for saving.
- When saving an object, you do a similar procedure, but instead of ‘images’, you use the collection ‘objects’. Then you set the data as object because it is already a map.
- Lastly, when saving a string, you do another similar procedure, but instead of ‘objects’, you use the collection ‘strings’. Then you set the data as a map containing the string.
After you’ve saved data to Firestore, you’ll be able to see the cloud persistence structure in the Firestore console.
But at this point, you still need to read back your data in order to see the effect of saving it.
Reading Your Data Online
In order to read your login and cart data back, update CloudPersistence
with the following methods:
// 1
@override
Future<Uint8List> getImage(String userId, String key) async {
final maxImageBytes = 30 * 1024 * 1024;
try {
final StorageReference storageReference =
FirebaseStorage().ref().child('$userId/$key');
final bytes = await storageReference.getData(maxImageBytes);
return bytes;
} catch (e) {
return null;
}
}
// 2
@override
Future<Map<String, dynamic>> getObject(String userId, String key) async {
final ref = await Firestore.instance
.collection('users')
.document(userId)
.collection('objects')
.document(key)
.get();
return ref.data;
}
// 3
@override
Future<String> getString(String userId, String key) async {
final ref = await Firestore.instance
.collection('users')
.document(userId)
.collection('strings')
.document(key)
.get();
if (ref.data != null) return ref.data['string'] as String;
return null;
}
To sum up, here’s what you did:
- First, when getting an image, try to get the data from Firebase storage at the same path where you saved it. Then, return it as bytes. Otherwise if there’s an exception, return null.
- When getting an object, read the data from
users/userId/objects/key
. Then return the data. - When getting a string, read the data from
users/userId/strings/key
. Then if it exists, return ‘string’ inside the data. Otherwise return null.
Build and run your project to see that cloud persistence works like the other methods of persistence, but this time the data is saved online. You were able to easily swap from local to remote persistence thanks to your use of the Repository
abstract class.
Removing Your Data Online
In order to complete the solution, you need to remove the items you stored online, e.g. when you remove them from the cart. Otherwise, you will see items in the cart with zero quantity.
In order to do this, add the following code, still in CloudPersistence/code>:
// 1
@override
Future<void> removeImage(String userId, String key) async {
try {
final StorageReference storageReference =
FirebaseStorage().ref().child('$userId/$key');
storageReference.delete();
} catch (e) {
return null;
}
}
// 2
@override
Future<void> removeObject(String userId, String key) async {
await Firestore.instance
.collection('users')
.document(userId)
.collection('objects')
.document(key)
.delete();
}
// 3
@override
Future<void> removeString(String userId, String key) async {
await Firestore.instance
.collection('users')
.document(userId)
.collection('strings')
.document(key)
.delete();
}
Here you are just deleting each image, object or string on the appropriate collection and document in Firestore, and in Firebase Storage for the image.
Build and run the app again, and you’ll be able to successfully add and remove items from the cart, even across logouts.
Finally, you finished; you persisted until the end. You wrote, you read, and you deleted. Congrats!
Where to Go From Here?
You can download the completed project by clicking on the Download Materials button at the top or bottom of the tutorial.
Flutter has done a cookbook on how to do some basics of data persistence. You can check it out here.
Look for a tutorial soon on data persistence with SQLite, another way to save data locally in your app. You can also read about using SQLite in Flutter in the official docs.
I hope you enjoyed this tutorial on Data Persistence on Flutter! If you have any questions or comments, please join the forum discussion below.