Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Firestore withConverter in Flutter

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,
});
like image 681
creativecreatorormaybenot Avatar asked Jun 22 '21 07:06

creativecreatorormaybenot


People also ask

How do I display data from firestore in flutter?

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.

How do I use Cloud Firestore in flutter?

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.

How do you modify a FireStore reference?

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.

How to store data in flutter using Moor?

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?

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.


Video Answer


2 Answers

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:

  • 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>.

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);
}
like image 188
creativecreatorormaybenot Avatar answered Oct 10 '22 09:10

creativecreatorormaybenot


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();
}
like image 21
iDecode Avatar answered Oct 10 '22 10:10

iDecode