void initState() {
// TODO: implement initState
super.initState();
getusers().then((dataf){
snapshots=dataf.documents[1];// In here
});
}
final db=Firestore.instance;
DocumentSnapshot snapshots;
Future<QuerySnapshot> getusers(){
return db.collection("Users").document(widget.userid).collection("UsersJoined").getDocuments();
}
I want to get all the documents and fields inside documents without referring to their index. Is it possible by a loop or something to loop through the whole documents and get all their fields
To loop over the documents in the QuerySnapshot:
getusers().then((dataf){
for (var userDoc in dataf.documents) {
print(userDoc["name"] // get the value of one field
var data = userDoc.data // get all data as a Map<String, Object>
})
});
Related
I am new to flutter and am writing a new app. In the app I want to retrieve the user data from a firebase document and populate TextFields on the user screen. I can pull the data fine using DocumentSnapShot. I want to use TextEditingController and populate it with the data but I am not able to do it. I know the data is there since I can see it in debug mode. Below are snippets of my code.
Here is where I get the data and populate the TextEditingControllers:
final agentsRef = FirebaseFirestore.instance.collection(('agents'));
getCurrentAgent() async {
final DocumentSnapshot currentAgent =
await agentsRef.doc(globals.currentUid).get();
if (currentAgent == null) {
emailController.text = "";
new Future.delayed(Duration.zero, () {
final agentProvider =
Provider.of<AgentProvider>(context, listen: false);
agentProvider.loadValues(Agents());
});
} else {
// existing record
// Updates Controllers
**emailController.text = currentAgent.data[index].toString(); // THIS LINE IS WHERE I AM HAVING TROUBLE**
// Updates State
new Future.delayed(Duration.zero, () {
final agentProvider =
Provider.of<AgentProvider>(context, listen: false);
agentProvider.loadValues(widget.agents);
});
}
#override
void initState() {
getCurrentAgent();
super.initState();
}
How do I access each data element in the DocumentSnapShot?
You'll typically get the fields by their name and not by their index. So:
**emailController.text = currentAgent.data()["email"].toString();
I've made a chat app and I want the user to be able to mark chatroom as favourite or mute the chatroom.
The structure of my database is like the image below
What I want is when user is trying to mute a chatroom or mark it as favourite,
I only update the item that matches the chatroomId, But after a lot of searching, I found out Firebase won't allow you to update an element of an array and you can only add or remove from an array.
This is my function to mute or mark a chatroom as favourite:
static final Firestore dbReference = Firestore.instance;
static Future<void> currentUserChatroomChangeMuteOrFavouriteMethod({
#required String chatroomId,
#required bool muteValue,
#required bool favouriteValue,
#required ChatroomMuteFavouriteOption operationToBeDone,
}) async {
DocumentReference docRef = dbReference
.collection("user_chatrooms")
.document(CurrentUserDetails.id);
DocumentSnapshot docSnapshot = await docRef.get();
Map<String, dynamic> docData = docSnapshot.data;
List<Map<String, dynamic>> userChatrooms =
(docData["chatrooms"] as List<dynamic>)
.map((chatroom) => Map<String, dynamic>.from(chatroom))
.toList();
for (int index = 0; index < userChatrooms.length; index++) {
Map<String, dynamic> chatroom = userChatrooms[index];
if (chatroom["chatroomId"] == chatroomId) {
if (operationToBeDone == ChatroomMuteFavouriteOption.muteOperation) {
chatroom["isMuted"] = !muteValue;
} else {
chatroom["isMarkedFavourite"] = !favouriteValue;
}
break;
}
}
await docRef.updateData({"chatrooms": userChatrooms});
}
So basically I get all the data, I update the field I want and I updateData again but this is not a good solution as I am also getting the chatrooms which I don't need.
How can I do this operation effeciently?
Thanks.
It's a perfectly fine solution. In order to update the contents of an array, you do have to read the document, modify the array in memory, then update the document with the new array. There's really no way to make this more efficient, other than converting your array to documents in a subcollection.
I have a Provider which has a method which takes data from Firebase as a stream, transforms it to a list and returns a Stream<List<Model>> . I'm trying to write a test where I want to check if the items in the List are the same as I expect them to be. How can I do that?
My Current Code:
test('getContacts returns a empty list when there is no contact',() async{
when(sharedPreferencesMock.get(any)).thenReturn('uid'); //mock the sharedprefs
documentSnapshot = DocumentSnapshotMock(); //mock documentsnapshot
when(documentSnapshot.exists).thenReturn(true); // this is done to pass the getUidByUsername method
documentReference = DocumentReferenceMock(documentSnapshotMock: documentSnapshot);
documentReference.setData({
'uid':'uid',
'contacts':[] // setting the usename in the data already so that duplicate contact exception is thrown
});
userDataProvider.getContacts().asBroadcastStream().listen((data){
expect(data.length,0);
});
});
And the provider method
#override
Stream<List<Contact>> getContacts() {
CollectionReference userRef = fireStoreDb.collection(Paths.usersPath);
DocumentReference ref =
userRef.document(SharedObjects.prefs.get(Constants.sessionUid));
return ref.snapshots().transform(StreamTransformer<DocumentSnapshot, List<Contact>>.fromHandlers(handleData: (documentSnapshot, sink) async{
List<String> contacts;
if (documentSnapshot.data['contacts'] == null) {
ref.updateData({'contacts': []});
contacts = List();
} else {
contacts = List.from(documentSnapshot.data['contacts']);
}
List<Contact> contactList = List();
for (String username in contacts) {
print(username);
String uid = await getUidByUsername(username);
DocumentSnapshot contactSnapshot = await userRef.document(uid).get();
contactList.add(Contact.fromFirestore(contactSnapshot));
}
sink.add(contactList);
}));
}
Update:
StreamController streamController = StreamController<List<Contact>>();
StreamSink<List<Contact>> sink = streamController.sink;
Stream<List<Contact>> stream = streamController.stream;
stream.listen((List<Contact> list){
expect(list.length,1);
});
userDataProvider.mapDocumentToContact(userCollection, userRef, documentSnapshot, sink);
streamController.close();
Make the lambda function that you currently pass to the StreamTansformer a separate function and test that.
If you want to test the full function there is a Firebase mock package on pub.
I am trying to get data from my Firestore and store the data into a String variable named time. Then put the variable into _event.
However, when I run the code, I got null, and I don't know why.
StateFulWidget...
String event;
Map<DateTime, List> _events;
#override
void initState() {
super.initState();
Firestore.instance.collection('events').document('2019-07-
30').get().then((
DocumentSnapshot ds) {
event = ds['time'];
});
_events = {
DateTime.parse("2019-08-01"): [event]
};
}
This is my Firestore document
I am new to Flutter and I have struggled for this question for a long time. Hope someone can help me
Thank you so much
UPDATE
I change my code.
However, I am still getting errors: The method [](Intance of 'DateTime') was called on null.
String event;
Map<DateTime, List> _events;
Future<String> getData()async{
final ds = await
Firestore.instance.collection('events').document('2019-07-30').get();
return ds['time'];
}
#override
void initState() {
super.initState();
getData().then((s) {
event = s;
//when I print(s), it showed the data correctly
});
_events = {
DateTime.parse("2019-08-01"): [event]
};
Can someone shows me where I got wrong?
Thank you!
You are not waiting for the result, ie _events = .. will run before .then() clause and that is why event variable is still null;
.get() is a Future so you need to await the result.
final ds = await Firestore.instance.collection('events').document('2019-07-
30').get();
event = ds['time'];
_events = {
DateTime.parse("2019-08-01"): [event]
};
This works for me :
Firestore().instance.collection('Collection Name')
.document('Document Name').get().then((data) async {
var dataReceive = data['property Name'];
}
I have an object Dish who containes a list of ingredients and I want to get them. How can I do?
In Firebase, Dish is a Document and Ingredient is a sub collection. I tried this but it doesn't work.
class Dish{
String name;
DocumentReference reference;
List<Ingredient> ingredients;
Dish.fromMap(Map<String, dynamic> map, {this.reference}){
this.name = map['name'];
this
.reference
.collection("ingredients")
.snapshots()
.listen((QuerySnapshot snap) {
final List<DocumentSnapshot> ingredientsDocuments = snap.documents;
List<Ingredient> ing = [];
for (var i = 0; i < ingredientsDocuments.length; i++) {
ing.add(Ingredient.fromSnapshot(ingredientsDocuments[i]));
}
this.ingredients = ing;
});
}
Dish.fromSnapshot(DocumentSnapshot snapshot)
: this.fromMap(snapshot.data, reference: snapshot.reference);
#override
String toString() => "Dish<$String>";
}
class Ingredient{
final String name;
final DocumentReference reference;
Ingredient.fromMap(Map<String, dynamic> map, {this.reference})
: assert(map['name'] != null),
name = map['name'];
Ingredient.fromSnapshot(DocumentSnapshot snapshot)
: this.fromMap(snapshot.data, reference: snapshot.reference);
#override
String toString() => "Ingredient<$String>";
}
How are you trying to fetch data from Firestore using Dish class? Were you using any asynchronous task(i.e. Future)? What's not working in your implementation? Any errors that you received?
Since I'm unable to run the repro you've provided, here's a sample code that you can try.
List<Ingredient> ingredients;
// call getDocuments() to fetch data from Firestore and add it to the List
Future<void> getDocuments() async {
ingredients = List();
var collection = FirebaseFirestore.instance
.collection('ingredients');
collection.get().then((value) {
value.docs.forEach((element) {
setState(() {
// add the object to the List
ingredients.add(Ingredient(Ingredient.fromMap(element.data())));
});
});
});
}
As for the Object, it can be as simple as this. No need to pass DocumentReference since we'll only be using it to map the data to the Object and be able to add it in the List.
class Ingredients {
var name;
Ingredients(Ingredients document) {
this.documentName = document.getName();
}
dynamic getName() => name;
Ingredients.fromMap(Map<dynamic, dynamic> document)
: name = document['name'];
}
You can check a working sample I've posted in here. It has pagination added, but it should have a similar approach with the code snippets I've shared here.