Flutter firebase get a field from document - firebase

I'm trying to get the message from a field in a collection. It is a read only data, i have modeled it like this
class SocialShare {
final String message;
SocialShare({
this.message,
});
factory SocialShare.fromJson(Map<String, dynamic> json) {
return SocialShare(
message: json['message'],
);
}
}
I have a collection named 'Social Share and contains a doc with a single field called message..
Here is how i call it
class SocialShares {
final CollectionReference _socialMessage =
FirebaseFirestore.instance.collection('socialShare');
Future<SocialShare> fetchsocial() {
return _socialMessage.get().then((value) {
return SocialShare.fromJson(value); // how can i call it
});
}
}
How can i get a that value from firebase

You can do fetchSocial async and await the result to return:
fetchSocial() async{
var value = await _socialMessage.get();
return SocialShare.fromJson(value);
}
then you have to call fetchSocial method with await or then where you need it.
await fetchSocial() or fetchSocial.then ...

The value in _socialMessage.get().then((value) { is a QuerySnapshot object, which contains the DocumentSnapshots of all documents in the socialShare collection.
To get a field, or the Map<String, dynamic> of all fields, you need the data from a single document. For example, to get the message field fro the first document from the collection, you can do:
return SocialShare.fromJson(value.docs[0].data());

Related

how to access a collection inside a firestore document and assign it as a list to my dart list inside my model?

how can i access a firestore collection inside a document and assign it to a list in my model, i tried accessing it like this snap.reference.collection('submittedUsers').get(); but i can't use async/await in constructors so i didn't knew what to do, this my model code:
final String fileUrl;
final String title;
final String description;
final List<String> submittedUsers;
LectureModel({
#required this.fileUrl,
#required this.title,
#required this.description,
this.submittedUsers,
}) : super(
fileUrl: fileUrl,
title: title,
description: description,
submittedUsers: submittedUsers,
);
Map<String, dynamic> toDocument() {
return {
'fileUrl': fileUrl,
'title': title,
'description': description,
};
}
factory LectureModel.fromSnapshot(DocumentSnapshot snap) {
// my submittedUsers collection is inside this `snap` document
// i want to get that collection and i assign it's memebers to my model submittedUsers list
final data = snap.data();
return LectureModel(
fileUrl: data['fileUrl'] as String,
title: data['title'] as String,
description: data['description'] as String,
);
}
}
Your data class looks just fine., but in your current structure, also add a list to your factory in the model, like this:
factory LectureModel.fromSnapshot(DocumentSnapshot snap, List<String> submittedUsersList) {
final data = snap.data();
return LectureModel(
fileUrl: data['fileUrl'] as String,
title: data['title'] as String,
description: data['description'] as String,
submittedUsers: submittedUsersList,
);
}
}
But you need to call your method LectureModel.fromSnapshot inside a function, or a future builder or stream builder for example. And also fetch the subcollection after getting the parent document
For example, you need a function like this, and put it in your widget where you need it.
Future<List<LectureModel>> getLectures() async {
QueryDocumentSnapshot snap = await FirebaseFirestore.instance.collection('NAME_OF_PARENT_COLLECTION').get();
List<LectureModel> lectureList=[];
//this will check that there actually is documents in firebase
if(snap.docs.isNotEmpty){
for(var singleSnapDocument in snap.docs){
//then you have to get the subcollection seperately for every
//document.
List<String> listOfsubmittedUsers =[];
listOfsubmittedUsers = await
FirebaseFirestore.instance.collection('NAME_OF_PARENT_COLLECTION')
.doc(singleSnapDocument.id).collection('submittedUsers')
.get().then((result)=> result.docs.map((e) => e.data().toString()).toList());
//this will add a LectureModel object into our list lectureList
lectureList.add(LectureModel.fromSnapshot(singleSnap, listOfsubmittedUsers));
}
}
print('Length of lectureList is: ' + lectureList.length.toString());
return lectureList;
}
Now, anywhere in your code, you can use onPressed or in initState and just call your function getLectures. i.e
onPressed: () async {List<LectureModel> listOfLecture = await getLectures();}
Your problem should be solved.

Fllutter/Firestore - realtime listening to collections and subcollections

I have a firebase collection named 'reviews' with a sub-collection 'clients'.
I am looking to fetch all reviews in realtime with their owners from Firebase Firestore but I got a bit lost when it came to correctly mapping the data and returning the listener's result.
This is 'reviews' model:
class Review {
final String reviewTitle;
final String reviewContent;
final String reviewCategory;
final String reviewTimestamp;
final int reviewVotesCount;
final Client client;
Review(
{this.reviewTitle,
this.reviewContent,
this.reviewCategory,
this.reviewTimestamp,
this.reviewVotesCount,
this.client});
}
This is the Service class:
class ReviewService {
var currentUser = FirebaseAuth.instance.currentUser;
var firestoreInstance = FirebaseFirestore.instance;
List<Review> fetchAllThreads() {
Review review;
Client client;
List<Thread> mReviewsList = new List<Review>();
firestoreInstance.collection('reviews').snapshots().listen((result) {
result.docs.forEach((result) {
firestoreInstance
.collection('reviews')
.doc(result.id)
.collection('clients')
.get()
.then((result) {/*here I get the result.data()*/});
});
});
}
Question after I get result.data() how can I map it to my model so I can add the result object to mReviewsList and then return mReviewsList ?
You can add a factory constructor in your Review class to create it from a Map and same applies for Client.
factory Review.fromMap(Map<String, dynamic> map) {
if (map == null) return null;
return Review(
reviewTitle: map['reviewTitle'],
reviewContent: map['reviewContent'],
reviewCategory: map['reviewCategory'],
reviewTimestamp: map['reviewTimestamp'],
reviewVotesCount: map['reviewVotesCount'],
client: Client.fromMap(map['client']),
);
}
If you're using VS Code, 'Dart Data Class Generator' extension can be handy there, and also there are multiple code generation packages in pub.dev for serialization and deserialization
Now in the place of your comment, you can do this:
mReviewsList.add(Review.fromMap(result.data()));
Update:
Based on Doug's comment, if you like to map your the data to your model and return a stream, you can create a helper function as follow:
Stream<List<T>> collectionStream<T>({
#required String path,
#required T builder(Map<String, dynamic> data),
}) {
final reference = FirebaseFirestore.instance.collection(path);
final snapshots = reference.snapshots();
return snapshots
.map((snapshot) => snapshot.docs.map((snapshot) => builder(snapshot.data())).toList());
}
To use it, simply call it as follow:
final stream = collectionStream<Review>(path: "reviews", builder: (data) => Review.fromMap(data));
if you like to fetch the data only once, you can create a helper function for that too:
Future<List<T>> getDocuments<T>({
String path,
#required T builder(Map<String, dynamic> data),
}) async {
final reference = FirebaseFirestore.instance.collection(path);
final snapshots = await reference.get();
final docs = snapshots.docs.map((doc) => builder(doc.data())).toList();
return docs;
}
and call it the same way:
final reviews = getDocuments<Review>(path: "reviews", builder: (data) => Review.fromMap(data));

How do I get the surrounding data related to my userId using flutter and firebase

While using flutter I am able to successfully get the UserId, however I want to be able get more user data (using the UserId)
Surrounding Information:
With the userId how would I go about printing the users; name bio, membership... etc?
Since you are using Realtime Database, then to get the other data, you can do the following:
db = FirebaseDatabase.instance.reference().child("Users");
db.once().then((DataSnapshot snapshot){
Map<dynamic, dynamic> values = snapshot.value;
values.forEach((key,values) {
print(values);
print(values["name"]);
});
});
First add a reference to node Users then use the forEach method to iterate inside the retrieved Map and retrieve the other values.
Try like this :
Future<dynamic> getWeightinKeg() async {
final DocumentReference document = Firestore.instance.collection('you_collection_name').document(user_id);
await document.get().then<dynamic>(( DocumentSnapshot snapshot) async {
final dynamic data = snapshot.data;
print(data['name'].toString())
//Do whatever you want to do with data here.
});
}
getUsers() async {
//here fbPath is reference to your users path
fbPath.once().then((user){
if(user.value !=null){
Map.from(user.value).forEach((k,v){
//here users is List<Map>
setState((){
users.add(v);
});
}
}
});
}
//Or
getUsers() async {
//here fbPath is reference to your users path
//and userListener is StreamSubscription
userListener = fbPath.onChildAdded.listen((user){
//here users is List<Map>
setState((){
users.add(Map.from(user.snapshot.value));
});
});
}
//and cancel in dispose method by calling
userListener.cancel();

flutter firestore, add new object in array

I have array of objects, I want to add new object when user enter new data in the array?
Firestore.instance.collection(city).document('Attractions').updateData(
"data", FieldValue.arrayUnion(obj)
);
This shows error, How can I achieve this with flutter?
Right Format is :
Firestore.instance.collection(city).document('Attractions').updateData({"data": FieldValue.arrayUnion(obj)});
updateData Take Map<String,dynamic> as data.
In your Code you are having , as separator between key - value instead it should be :
#anmol.majhail 's is right, but to solve #Sami Ullah's problem, you must first make a list and add the object into the list like this:
var list = [objectBeingAdded];
Firestore.instance.collection('city').document('Attractions').updateData({"data": FieldValue.arrayUnion(list)});
Null safe code:
Say this is the data you want to add
Map<String, dynamic> someData = {
'foo': 1,
'bar': true,
};
Add the data with unique auto-generated ID:
var collection = FirebaseFirestore.instance.collection('collection');
collection
.add(someData)
.then((_) => print('Added'))
.catchError((error) => print('Add failed: $error'));
Add the data with your own ID:
var collection = FirebaseFirestore.instance.collection('collection');
collection
.doc('document_id') // <-- Document ID
.set(someData)
.then((_) => print('Added'))
.catchError((error) => print('Add failed: $error'));
Add the object to an array:
var collection = FirebaseFirestore.instance.collection('collection');
collection
.doc('document_id') // <-- Document ID
.set({'data': FieldValue.arrayUnion(list)}) // <-- Add data
.then((_) => print('Added'))
.catchError((error) => print('Add failed: $error'));
This is a working function I built that adds new Maps to an array in my Firestore Services class. I'm using Json Serializable to annotate all my model classes. userTemplateSections is a data field in my userTemplate firestore documents. I take userTemplate as a constructor of the 'addUserTemplateSection' function to make sure I'm editing the correct document.
I also added the function I made to delete Maps from a firestore document array.
'''
Future<void> addUserTemplateSection(
{UserTemplate userTemplate, String title, String summary}) async {
try {
final UserTemplateSection userTemplateSection =
UserTemplateSection(title: title, summary: summary);
await _firestore
.document(FirestorePath.userTemplate(uid, userTemplate.id))
.updateData(
{
'userTemplateSections':
FieldValue.arrayUnion([userTemplateSection.toJson()])
},
);
} catch (e) {
print(e);
}
}
'''
'''
Future<void> deleteUserTemplateSection({
UserTemplate userTemplate,
UserTemplateSection userTemplateSection,
}) async {
try {
await _firestore
.document(FirestorePath.userTemplate(uid, userTemplate.id))
.updateData(
{
'userTemplateSections':
FieldValue.arrayRemove([userTemplateSection.toJson()])
},
);
} catch (e) {
print(e);
}
}
'''

Waiting for result of Future<DocumentSnapshot> in Flutter Firestore

I have a cloud FireStore database with 2 fields.
imageUrl (url of a remote file)
user (reference field of a document in users collection)
Below is how I get the documents from the images collection.
class ImagePost {
final String imageUrl;
final User user;
const ImagePost(
{this.imageUrl,
this.user});
factory ImagePost.fromDocument(DocumentSnapshot document) {
User userInfo;
DocumentReference userReference = document['user'];
Future<DocumentSnapshot> userRef = userReference.get();
userRef.then((document) {
userInfo = User.fromJSON(document.data);
});
ImagePost post = new ImagePost(
imageUrl: document['imageUrl'],
user: userInfo // ==> always null while returning
);
return post;
}
}
When getting the reference user document, the post object always contains null value for user field. I expect the user object to be populated.
But the user value is retrieved late and not returned along with the post object.
How can I ensure that user value is retrieve before returning the post value?
That's because the get() method return a Future and you need to use async 'await' in order to wait for the response , but is not posible to use it in your constructor.
Just create a method (not constructor) and use like this :
Future<ImagePost> getImagePostFromDocument(DocumentSnapshot document) async {
DocumentReference userReference = document['user'];
DocumentSnapshot userRef = await userReference.get();
User userInfo = User.fromJSON(userRef);
ImagePost post = new ImagePost(
imageUrl: document['imageUrl'],
user: userInfo
);
return post;
}
I recommend you to call it form a FutureBuilder

Resources