Firestore types
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
.
Default
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:
final modelRef = FirebaseFirestore.instance
.collection('models')
.doc('123')
.withConverter<Model>(
fromFirestore: (snapshot, _) => Model.fromJson(snapshot.data()!),
toFirestore: (model, _) => model.toJson(),
);
final modelsRef =
FirebaseFirestore.instance.collection('models').withConverter<Model>(
fromFirestore: (snapshot, _) => Model.fromJson(snapshot.data()!),
toFirestore: (model, _) => model.toJson(),
);
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>
.
Usage
The usage of the method is straight-forward:
- You pass a
FromFirestore
function that converts a snapshot (with options) to your custom model.
- You pass a
ToFirestore
function that converts your model T
(with options) back to a Map<String, dynamic>
, i.e. Firestore-specific JSON data.
Example
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);
}