Flutter Firestore with Streams getting type '_JsonQueryDocumentSnapshot' is not a subtype' error?

205

As we discussed in the other question, you need to extract the data from the document as follow:

Stream<List<DogModel>> getDogs(String uid) {
return dogCollection.where('userId', isEqualTo: uid).snapshots().map(
      (snapshot) => snapshot.docs
          .map((document) => DogModel.fromJson(document.data()) // <~~ here
          .toList(),
    );

As you see, you should call document.data() (not document.data, which was my mistake earlier).

Share:
205
Almog
Author by

Almog

Software Engineer. #Flutter &amp; #Dart Developer Startup Founder Scuba Diver. Hiker. Lifesaver. Search &amp; Rescue Dog Handler. Blog https://almog.io/blog #flutterdev

Updated on December 30, 2022

Comments

  • Almog
    Almog over 1 year

    I'm getting the following error when trying to work with Firestore and Stream Provider

    type '_JsonQueryDocumentSnapshot' is not a subtype of type 'Map<String, dynamic>' in type cast
    

    Here is my root page

    ChangeNotifierProxyProvider<AuthProvider, DogProvider>(
          update: (context, value, previous) => DogProvider(), // ..getDogList()
          create: (_) => DogProvider(), //..getDogList()
        ),
        StreamProvider<Object>(
          create: (context) =>
              DogFirestoreService().getDogs('GeVnAbdq9BWs1STbytlAU65qkbc2'),
          initialData: 10,
          child: const DogsListScreen(),
        ),
    

    My Firestore Service getting the stream

    Stream<List<DogModel>> getDogs(String uid) {
    return dogCollection.where('userId', isEqualTo: uid).snapshots().map(
          (snapshot) => snapshot.docs
              .map((document) => DogModel.fromFire(document))
              .toList(),
        );
    

    }

    The model

     @JsonSerializable(explicitToJson: true)
    class DogModel {
      String? dogImage;
      String dogName;
      DogBreed? breedInfo;
      @JsonKey(fromJson: dateTimeFromTimestamp, toJson: dateTimeAsIs)
      DateTime? dogBirthday;
      double? dogWeight;
      DogWeight? weightType;
      DogGender? dogGender;
      List<String>? dogType;
      DogStatus? dogStatus;
      bool? registered;
      String? dogChipId;
      @JsonKey(fromJson: dateTimeFromTimestamp, toJson: dateTimeAsIs)
      DateTime? createdAt;
      DogStats? dogStats;
      @JsonKey(ignore: true)
      String? id;
      String? userId;
    
      DogModel({
        required this.dogImage,
        required this.dogName,
        required this.breedInfo,
        required this.dogBirthday,
        required this.dogWeight,
        required this.weightType,
        required this.dogGender,
        required this.dogType,
        required this.dogStatus,
        required this.registered,
        this.dogStats,
        this.dogChipId,
        this.createdAt,
        this.userId,
        this.id,
      });
    
      factory DogModel.fromFire(QueryDocumentSnapshot snapshot) {
        return DogModel.fromJson(snapshot as Map<String, dynamic>);
      }
    
      // JsonSerializable constructor
      factory DogModel.fromJson(Map<String, dynamic> json) =>
          _$DogModelFromJson(json);
    
      // Serialization
      Map<String, dynamic> toJson() => _$DogModelToJson(this);
    }
    

    And in the widget

    Widget build(BuildContext context) {
    final test = context.watch<Object>();
    print(test);
    
  • Almog
    Almog over 2 years
    Yes I did that the issue is with factory DogModel.fromFire "type '_JsonQueryDocumentSnapshot' is not a subtype of type 'Map<String, dynamic>' in type cast" I think I need to copy the keys but not how
  • osaxma
    osaxma over 2 years
    @Almog did you try DogModel.fromJson(document.data() as Map<String, dynamic>) instead of of DogModel.fromFire? .. something weird happening there :/
  • Almog
    Almog over 2 years
    Wow that was it, I think its good now
  • osaxma
    osaxma over 2 years
    @Almog I don't think you even need the as Map<String, dynamic> cast btw..
  • Almog
    Almog over 2 years
    The quick question around best practices, I have my Firestore service file and provider which is separated for adding dogs, update everything goes the provider but for the stream, it's directly to firestore class is that correct? And second, should I only be calling the stream inside the correct widget not the root
  • osaxma
    osaxma over 2 years
    @Almog 1-you can create an abstract class (e.g. DogQueries) and inside it you put the methods that is used in the app (e.g. Stream<List<DogModel>> getDogs();). Then create an implementation for that class specific to firestore (DogQueriesFirestore). This helps so even if you wanna change the database later, you just create a new implementation. 2-, I believe StreamProvider will handle the subscription correctly. As long as the tree above it is not changing, so it'll not create a new subscription after each build. So just place it as high as necessary in the widget tree.
  • osaxma
    osaxma over 2 years
    @Almog The only case, that I can think of, where you need to place it at the root is when the StreamProvider is needed at multiple pages (because when you navigate to pages, you navigate from the root widget).
  • Almog
    Almog over 2 years
    Yes what I thought I don't think it needs to be in the root I have the ChangeNotifierProxyProvider in the and not even sure that is correct