I saw that recently the cloud_firestore: ^2.0.0
update brought a new withConverter
functionality.
I want to use it to retrieve and pass my models from and to Firestore, but I am not quite sure how to do this with my existing code.
For example, how do I update the following code to use my custom models?
FirebaseFirestore.instance.collection('movies').add({
'length': 123,
'rating': 9.7,
});
There should be array called contacts and inside that there should be 3 maps according to your data. Then create a list in your screen class. List contacts; Then create a function to retrieve data from firestore.
To start using the Cloud Firestore package within your project, import it at the top of your project files: Before using Firestore, you must first have ensured you have initialized FlutterFire.
By default, Firestore references manipulate a Map<String, dynamic> object. The downside is that we lose type safety. One solution is to use withConverter, which will modify methods like CollectionReference.add or Query.where to be type-safe.
If you want to store the data locally inside your app you can read my post on the moor library. MOOR is a library allowing us to work with the Flutter’s SQLite database fluently and in pure Dart… Setting up the project. Adding required dependencies. Creating a collection on the cloud firestore. Uploading data. Fetching data. Designing the UI.
How to integrate firebase with flutter android or ios app ? Open firebase, click on get started which will take you to the Add Project screen. Enter the project name and complete the other essential steps. It will take you to your project screen, click on the android icon.
To begin, starting with 2.0.0
, all Firestore references & queries are now typed. This means that CollectionReference<T>
, DocumentReference<T>
, and Query<T>
now all have a generic type parameter T
.
By default (e.g. when calling FirebaseFirestore.instance.collection()
), this generic is Map<String, dynamic>
. This means that (without calling withConverter
), the data you pass and the data you receive is always of type Map<String, dynamic>
.
withConverter
Now, you can make use of withConverter
in various places in order to change this type:
DocumentReference.withConverter
:final modelRef = FirebaseFirestore.instance
.collection('models')
.doc('123')
.withConverter<Model>(
fromFirestore: (snapshot, _) => Model.fromJson(snapshot.data()!),
toFirestore: (model, _) => model.toJson(),
);
CollectionReference.withConverter
:final modelsRef =
FirebaseFirestore.instance.collection('models').withConverter<Model>(
fromFirestore: (snapshot, _) => Model.fromJson(snapshot.data()!),
toFirestore: (model, _) => model.toJson(),
);
Query.withConverter
:final personsRef = FirebaseFirestore.instance
.collection('persons')
.where('age', isGreaterThan: 0)
.withConverter<Person>(
fromFirestore: (snapshot, _) => Person.fromJson(snapshot.data()!),
toFirestore: (model, _) => model.toJson(),
);
What happens when calling withConverter
is that the generic type T
is set to your custom Model
(e.g. Person
in the last example). This means that every subsequent call on the document ref, collection ref, or on the query will all work with that type instead of Map<String, dynamic>
.
The usage of the method is straight-forward:
FromFirestore
function that converts a snapshot (with options) to your custom model.ToFirestore
function that converts your model T
(with options) back to a Map<String, dynamic>
, i.e. Firestore-specific JSON data.Here is an example of using withConverter
with a custom Movie
model class (note that I am using freezed
because it is more readable):
Future<void> main() async {
// Create an instance of our model.
const movie = Movie(length: 123, rating: 9.7);
// Create an instance of a collection withConverter.
final collection =
FirebaseFirestore.instance.collection('movies').withConverter(
fromFirestore: (snapshot, _) => Movie.fromJson(snapshot.data()!),
toFirestore: (movie, _) => movie.toJson(),
);
// Directly add our model to the collection.
collection.add(movie);
// Also works for subsequent calls.
collection.doc('123').set(movie);
// And also works for reads.
final Movie movie2 = (await collection.doc('2').get()).data()!;
}
@freezed
class Movie with _$Movie {
const factory Movie({
required int length,
required double rating,
}) = _Movie;
factory Movie.fromJson(Map<String, dynamic> json) => _$MovieFromJson(json);
}
Let's assume you've a model like this:
class Movie {
final int length;
final double rating;
const Movie({required this.length, required this.rating});
factory Movie.fromJson(Map<String, dynamic> json) => Movie(
length: json['length'],
rating: json['rating'],
);
Map<String, Object?> toJson() => {
'length': length,
'rating': rating,
};
}
You can use withConverter
like:
void main() async {
final model = FirebaseFirestore.instance
.collection('movies')
.doc()
.withConverter<Movie>(
fromFirestore: (snapshot, _) => Movie.fromJson(snapshot.data()!),
toFirestore: (movie, _) => movie.toJson(),
);
final movie = Movie(length: 123, rating: 9.7);
// Write operations
await model.set(movie);
await model.delete();
await model.update(newMovie);
// Read operation
final fetchedMovie = (await model.get()).data();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With