How to store data into model using fromJson in flutter firebase? - firebase

Currently I'm working on a flutter project. I'm trying to create a function that gets snapshot from Firebase document and stores it into userData model using fromJson function.
Here's my User.dart
class UserData {
// final String uid;
final String usn;
final String fullName;
final int? sem;
final String branch;
final String section;
UserData({
required this.usn,
required this.fullName,
required this.sem,
required this.branch,
required this.section,
});
factory UserData.fromJson(Map<String, dynamic> json) {
return UserData(
usn: json['usn'],
fullName: json['fullName'],
sem: json['sem'],
branch: json['branch'],
section: json['section']);
}
Map<String, dynamic> toMap() {
return {
'usn': usn,
'fullName': fullName,
'sem': sem,
'branch': branch,
'section': section
};
}
}
In my database.dart
I want to create a Stream function in it to map to fromJson
Here's what I have tried
Stream<UserData?> curUserData() {
return _db.collection('users').doc(user.uid).snapshots()
.map((DocumentSnapshot snapshot) => UserData.fromJson(snapshot.data()));
}
Can anyone rectify the function?
I am getting error from above function.

Your problem is just wrong type assertion. Try this:
Change DocumentSnapshot for DocumentSnapshot<Map<String, dynamic>>
Stream<UserData?> curUserData() {
return _db.collection('users').doc(user.uid).snapshots()
.map((DocumentSnapshot<Map<String, dynamic>> snapshot) => UserData.fromDocument(snapshot.data()!));
}

This example is for my case with cloud_firestore: ^3.1.7:
getLastStageNumber() async {
QuerySnapshot querySnapshot = await stagesRef
.orderBy('stage_number', descending: true)
.limit(1)
.get();
StageMod stageMod;
if (querySnapshot.docs.isNotEmpty) {
stageMod = StageMod.fromJson(
querySnapshot.docs.first.data()! as Map<String, dynamic>);
lastStageNumber = stageMod.stageNumber;
}
}

Related

Flutter How to model any class instead of FutureBuilder?

I have a model and I want to use my services file to fill it from Firebase but I don't know how to do that ?
I am filling it with FutureBuilder that's okey. But it is exhausting me.
Here is my model:
class ReviewModel {
String? uid;
String? userID;
String? comment;
dynamic rate;
ReviewModel({
this.uid,
this.userID,
this.comment,
this.rate,
});
Map<String, dynamic> toMap() {
return {
'uid': uid,
'userID': userID,
'comment': comment,
'rate': rate,
};
}
factory ReviewModel.fromMap(Map<String, dynamic> map) {
return ReviewModel(
uid: map['uid'],
userID: map['userID'],
comment: map['comment'],
rate: map['rate'],
);
}
factory ReviewModel.fromDatabase(
DocumentSnapshot snapshot, Map<String, dynamic> map) {
return ReviewModel(
uid: snapshot['uid'],
userID: map['userID'],
comment: map['comment'],
rate: map['rate'],
);
}
}
Code is Following below,
Future<ReviewModel> getSalonReviews(String salonUID) async {
CollectionReference aRef = FirebaseFirestore.instance
.collection("salons")
.doc(salonUID)
.collection('bucket')
.doc('reviewbox')
.collection('reviews');
dynamic _doc;
var snapshot;
try {
await aRef.get().then((querySnapshot) => {
for (var dummyDoc in querySnapshot.docs)
{
_doc = dummyDoc.data(),
print(_doc),
}
});
return ReviewModel.fromMap(_doc);
} on FirebaseException catch (e) {
Get.snackbar("Hata", e.code);
rethrow;
}
}
This code is not returning my ReviewModel.
Also I am using GetX and this is my GetX code:
final Rx<ReviewModel> _reviewModel = ReviewModel().obs;
ReviewModel get reviewModel => _reviewModel.value;
set reviewModel(ReviewModel value) => _reviewModel.value;
Future fillReviewModel(String uid) async {
SalonController.instance.reviewModel =
await FakeService().getSalonReviews(uid);
}
it return me this:
And this is my Firebase docs:
How do I achive my ReviewModel with Obx. If I try it, it returns null.
You don't have to return a model you'll do something like this in your prvoider file:
List _reviews = [];
List get reviews => [..._reviews];
// IN your future void function
Future<void> myFunction () async{
myReviews = ...result of forEach;
// now update _reviews
_reviews = [...myReviews];
//And then notify listeners
notifylisteners;
}
And then in your futurebuilder
FutureBuilder(future: Provider.of<myClass>(context, listen:false).myFunction(),
builder:(context, snapshot){
// check the state like the following
if(snapshot.connectionState == ConnectionState.done){
final myValues = Provider.of<myClass>(context, listen:false).reviews;
...do something
return your_values}
if(snapshot.connectionState == ConnectionState.waiting){return progressIndicator}
})

Flutter Firestore: Building my model from a snapshot

I'm struggling to build my model from firestore map. It seems basic yet I can't figure it out.
Here's my code :
firestore_service.dart
{
// ...
final FirebaseFirestore _db = FirebaseFirestore.instance;
await _db
.collection('Users')
.doc('jWtp19r7g2b4wwMJ5yBi')
.get()
.then((snapshot) {
print(snapshot);
print(snapshot.runtimeType);
final _data = snapshot.data() ?? {};
print(_data);
print(_data['uid']);
final user = UserModel.fromJson(_data);
print(user.uid);
return user;
});
}
user_model.dart
import 'package:flutter/material.dart';
class UserModel {
// ...
factory UserModel.fromJson(Map<String, dynamic> json) {
return UserModel(
uid: json['uid'],
username: json['username'],
characterID: json['character_id'],
);
}
}
Terminal
Can anyone help me?
Okay so, I don't know if it's a "clean" way of doing it but I figured I could add another factory in my model to build specifically from Firestore.
Since the specific error was Unhandled Exception: type 'List<dynamic>' is not a subtype of type 'List<String>', I duckduckgo-ed the error and found this solution :
service.dart
await _db
.collection('Users')
.doc('jWtp19r7g2b4wwMJ5yBi')
.get()
.then((snapshot) {
final _data = Map<String, dynamic>.from(snapshot.data() ?? {});
print(_data.runtimeType);
final user = UserModel.fromFirestore(_data);
print(user.uid);
});
user_model.dart
class UserModel {
UserModel({
required this.uid,
required this.characterID,
this.username,
});
final String uid;
final String? username;
final List<String> characterID;
factory UserModel.fromJson(Map<String, dynamic> json) {
return UserModel(
uid: json['uid'],
username: json['username'],
characterID: json['character_id'],
);
}
factory UserModel.fromFirestore(Map<dynamic, dynamic> json) {
return UserModel(
uid: json['uid'],
username: json['username'],
characterID: List<String>.from(json['character_id']),
);
}
Map<String, dynamic> toJson() => {
"uid": uid,
"username": username,
"character_id": characterID,
};
}
I feel like it's not my factory's role to "filter / cast" the data but that's the only way I found it working.

Flutter Assigning QuerySnapshot to Map<dynamic, dynamic>

I am trying to fetch some menu item details such as menu name, slogan text icon, and verify data from firebase firestore using flutter but in the services of database getMenus() function, I am facing an error.
I have the following class model
import 'dart:convert';
MenuModel medicalTestModelFromJson(String str) => MenuModel.fromJson(json.decode(str));
String medicalTestModelToJson(MenuModel data) => json.encode(data.toJson());
class MenuModel {
MenuModel({
required this.title,
required this.slogan,
required this.icon,
required this.verify,
});
String title;
String slogan;
String icon;
bool verify;
factory MenuModel.fromJson(Map<String, dynamic> json) => MenuModel(
title: json["title"],
slogan: json["slogan"],
icon: json["icon"],
verify: json["verify"],
);
Map<String, dynamic> toJson() => {
"title": title,
"slogan": slogan,
"icon": icon,
"verify": verify,
};
}
And the following is the menu collection services from firebase firestore
class MenuServices{
static Future<List<MenuModel>> getMenus() async {
QuerySnapshot menuSnapshot = await FirebaseFirestore.instance.collection('homeItems').where("verify", isEqualTo: true).get();
List<MenuModel> menus =[];
Map<dynamic, dynamic> values = menuSnapshot.docs; //(***My Error is Exactly in this line which menuSnapshot.docs is not recognizing***)
values.forEach((key, values) {
menus.add(MenuModel.fromJson(values));
});
return menus;
}
}
And the error is
A value of type 'List<QueryDocumentSnapshot<Map<String, dynamic>>>' can't be assigned to a variable of type 'Map<dynamic, dynamic>
Change it to this:
class MenuServices{
static Future<List<MenuModel>> getMenus() async {
QuerySnapshot menuSnapshot = await FirebaseFirestore.instance.collection('homeItems').where("verify", isEqualTo: true).get();
List<MenuModel> menus = menuSnapshot.docs.map(
(e)=> MenuModel.fromJson(e.data() as Map<String, dynamic>)).toList();
return menus;
}
}
I used this and it worked:
void getLeituras(QuerySnapshot snapshot){
List leituras = [];
Leitura leitura;
snapshot.docs.forEach((e) {
leitura = Leitura();
leitura = Leitura.fromJson(e.data as Map<String, dynamic>);
leituras.add(leitura);
});
}

Flutter Firebase A value of type 'List<Future<CustomClass>>' can't be assigned to a variable of type 'List< CustomClass >?'

I am having an issue with my FutureBuilder in flutter. It's supposed to map the custom class 'GeoPost' to a 'List' from a firebase query but somewhere in my code it is not reading the Geopost list as a non Future
here is the code where the error occurs:
FutureBuilder(
future: postsRef.get().then((doc) {
geoPosts = doc.docs.map((doc) async => await GeoPost.fromDoc(doc)).toList();
}),
builder: (context, snapshot) { }
custom class and functions:
class GeoPost {
final String caption;
final String postId;
final String authorId;
final int likesCount;
final int commentCount;
final Position position;
final Timestamp timeStamp;
final String? imageUrl;
final Userdata authordata;
GeoPost(this.caption, this.postId, this.authorId, this.likesCount, this.commentCount, this.position, this.timeStamp, this.imageUrl, this.authordata);
static Future<GeoPost> fromDoc(DocumentSnapshot doc) async {
return GeoPost.fromDocument(doc, await getPostUserDataFromFirestore(doc['userId']));
}
factory GeoPost.fromDocument(DocumentSnapshot doc, Userdata author) {
return GeoPost(
doc['caption'],
doc['postId'],
doc['userId'],
doc['likeCount'] ?? 0,
doc['commentCount'] ?? 0,
doc['position'],
doc['timeStamp'],
doc['imageUrl'],
author
);
}
Future<void> getPostUserDataFromFirestore (String did) async {
return Userdata.fromDoc(await usersRef.doc(uid).get());
}
}
error:
Error: A value of type 'List<Future<GeoPost>>' can't be assigned to a variable of type 'List<GeoPost>?'.
You need to wait for all the Futures in the List<Future<GeoPost> (which converts them into a List<GeoPost>) before assiging them to a variable of type List<GeoPost>.
Update your FutureBuilder code to this:
FutureBuilder<List<GeoPost>>(
future: () async {
var doc = await FirebaseFirestore.instance.collection('collectionPath').get();
var data = doc.docs.map((doc) async => await GeoPost.fromDoc(doc)).toList();
geoPosts = await Future.wait(data);
return geoPosts;
}(),
builder: (context, snapshot) { }
Also, you need to change the return type of the getPostUserDataFromFirestore method from Future<void> to Future<Userdata>. void is used when there is no value returned.
Update the getPostUserDataFromFirestore method to this:
Future<Userdata> getPostUserDataFromFirestore (String did) async {
return Userdata.fromDoc(await usersRef.doc(uid).get());
}

Flutter Firestore Read specific field from specific document via stream function

basically im struggling to create a function in my database.dart where i can call this function to display a specfic field in a specific document.
Example, i want to use Database.readUserProfileData('Profile', 'surname') to display the string/alt to a widget.
static Future<QuerySnapshot> readUserProfileData({
required String docId,
required String field,
}) async {
var ref = _mainCollection.get().then((querySnapshot) {
querySnapshot.docs.forEach((result) {
Object? data = result.data();
print(data);
});
});
}
Here is my database.dart
(please ignore the UpdateItem function as i have not configured it properly as i copied this from a template, or maybe update it for me lol sorry ;))
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
/* ------------------ Database Reference - Main Collection ------------------ */
final CollectionReference _mainCollection = _firestore.collection('_TestFB');
class Database {
static String? userUid;
/* -------------------------- Create User Database -------------------------- */
static Future<void> createUserDataFile({
required String uid,
required String surname,
required int mobile,
}) async {
DocumentReference documentReferencer =
_mainCollection.doc('UserData').collection(uid).doc('Profile');
Map<String, dynamic> data = <String, dynamic>{
"surname": surname,
"mobile": mobile,
};
_mainCollection
.doc('UserData')
.collection(uid)
.limit(1)
.get()
.then((snapshot) async {
if (snapshot.size == 1) {
print("**User Profile Exists");
} else {
await documentReferencer
.set(data)
.whenComplete(() => print("**New Profile Created for - " + uid))
.catchError((e) => print(e));
}
});
}
/* ------------------------- Read User Profile Data ------------------------- */
static Future<QuerySnapshot> readUserProfileData({
required String docId,
required String field,
}) async {
var ref = _mainCollection.get().then((querySnapshot) {
querySnapshot.docs.forEach((result) {
Object? data = result.data();
print(data);
});
});
}
Thanks in Advance
If you already have the document id, then just do:
static Future<DocumentSnapshot> readUserProfileData({
required String docId,
required String field,
}) async {
var ref = _mainCollection.doc(docId).get().then((snapshot) {
print(snapshot);
print(snapshot.data()[field]);
});
}
You don't need to use forEach since you are only retrieving one document and get() will have a return type Future<DocumentSnapshot>

Resources