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).
Author by
Almog
Software Engineer. #Flutter & #Dart Developer Startup Founder Scuba Diver. Hiker. Lifesaver. Search & Rescue Dog Handler. Blog https://almog.io/blog #flutterdev
Updated on December 30, 2022Comments
-
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 over 2 yearsYes 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 over 2 years@Almog did you try
DogModel.fromJson(document.data() as Map<String, dynamic>)
instead of ofDogModel.fromFire
? .. something weird happening there :/ -
Almog over 2 yearsWow that was it, I think its good now
-
osaxma over 2 years@Almog I don't think you even need the
as Map<String, dynamic>
cast btw.. -
Almog over 2 yearsThe 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 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 believeStreamProvider
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 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 over 2 yearsYes 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