How do I get data from multiple documents with StreamBuilder? - firebase

I am trying to make my Flutter app with chat.
I use a streambuilder to update the chat data.
My problem is i don't know how i can read multiple documents with streambuilder.
Here my database:
My plan i would like get all data from this documents.
Before, i storage the id in a object and i use a for loop to get all data from documents.
The documents can be a random count (2 or 10...).
Here my streambuilder:
body() {
//build stream link get id
Stream _build_stream_id() async* {
//load user
var user_id = await StorageUserID.loadData();
yield* FirebaseFirestore.instance
.collection('chat')
.doc('users')
.collection(user_id)
.snapshots();
}
//build stream link get data
Stream _build_stream_data(chat_overview_object, index) async* {
yield* FirebaseFirestore.instance
.collection('chat')
.doc('chat_overview_data')
.collection('data')
.doc(chat_overview_object[index].chat_overview_id[0])
.snapshots();
}
return StreamBuilder(
stream: _build_stream_id(),
builder: (context, snapshot) {
if (snapshot.hasData) {
var chat_overview_object = query_chat_overview_data_1(snapshot.data);
for (var i = 0; i < chat_overview_object.length; i++) {
return StreamBuilder(
stream: _build_stream_data(chat_overview_object, i),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text('test: ' + snapshot.data.toString());
} else {
return Text("No data");
}
},
);
}
} else {
return Text("No data");
}
return Text('data');
},
);
}
If you find a better way pls tell me.
If you have questions, feel free to ask me.
Many thx (:

You can combine streams using the StreamGroup from the async package.
import 'package:async/async.dart' show StreamGroup;
List<Stream> streamList = [stream1, stream2];
Stream combinedStream = StreamGroup.merge(streamList);
Though handling Firestore sollections with different fields might be tricky.

Related

How to get specific document data from firebase and return as a string

I am trying to get a data in firestore like this:
am trying to get FCMToken from a user document
And be able to immediately use it as a string inside a streambuilder
FirebaseFirestore.instance.collection("usersToken")
.doc(snapshot.data.docs[index].data()['userId'].toString())
.get();
//I don't know what to do from here on to be able to get it as a string
How do I do it?
It works for me
Widget build(BuildContext context) {
return new StreamBuilder(
stream: Firestore.instance.collection('COLLECTION_NAME').document('TEST').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
}
var userDocument = snapshot.data;
return new Text(userDocument["property_name"]);
}
);
}

How can i get data from firebase by document ID in Flutter? [duplicate]

Edit: This Question is outdated, and I am sure, new documentation and more recent answers are available as of now.
I want to retrieve data of only a single document via its ID. My approach with example data of:
TESTID1 {
'name': 'example',
'data': 'sample data',
}
was something like this:
Firestore.instance.document('TESTID1').get() => then(function(document) {
print(document('name'));
}
but that does not seem to be correct syntax.
I was not able to find any detailed documentation on querying firestore within flutter (dart) since the firebase documentation only addresses Native WEB, iOS, Android etc. but not Flutter. The documentation of cloud_firestore is also way too short. There is only one example that shows how to query multiple documents into a stream which is not what i want to do.
Related issue on missing documentation:
https://github.com/flutter/flutter/issues/14324
It can't be that hard to get data from a single document.
UPDATE:
Firestore.instance.collection('COLLECTION').document('ID')
.get().then((DocumentSnapshot) =>
print(DocumentSnapshot.data['key'].toString());
);
is not executed.
but that does not seem to be correct syntax.
It is not the correct syntax because you are missing a collection() call. You cannot call document() directly on your Firestore.instance. To solve this, you should use something like this:
var document = await Firestore.instance.collection('COLLECTION_NAME').document('TESTID1');
document.get() => then(function(document) {
print(document("name"));
});
Or in more simpler way:
var document = await Firestore.instance.document('COLLECTION_NAME/TESTID1');
document.get() => then(function(document) {
print(document("name"));
});
If you want to get data in realtime, please use the following code:
Widget build(BuildContext context) {
return new StreamBuilder(
stream: Firestore.instance.collection('COLLECTION_NAME').document('TESTID1').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return new Text("Loading");
}
var userDocument = snapshot.data;
return new Text(userDocument["name"]);
}
);
}
It will help you set also the name to a text view.
Null safe code (Recommended)
You can either query the document in a function (for example on press of a button) or inside a widget (like a FutureBuilder).
In a method: (one time listen)
var collection = FirebaseFirestore.instance.collection('users');
var docSnapshot = await collection.doc('doc_id').get();
if (docSnapshot.exists) {
Map<String, dynamic>? data = docSnapshot.data();
var value = data?['some_field']; // <-- The value you want to retrieve.
// Call setState if needed.
}
In a FutureBuilder (one time listen)
FutureBuilder<DocumentSnapshot<Map<String, dynamic>>>(
future: collection.doc('doc_id').get(),
builder: (_, snapshot) {
if (snapshot.hasError) return Text ('Error = ${snapshot.error}');
if (snapshot.hasData) {
var data = snapshot.data!.data();
var value = data!['some_field']; // <-- Your value
return Text('Value = $value');
}
return Center(child: CircularProgressIndicator());
},
)
In a StreamBuilder: (always listening)
StreamBuilder<DocumentSnapshot<Map<String, dynamic>>>(
stream: collection.doc('doc_id').snapshots(),
builder: (_, snapshot) {
if (snapshot.hasError) return Text('Error = ${snapshot.error}');
if (snapshot.hasData) {
var output = snapshot.data!.data();
var value = output!['some_field']; // <-- Your value
return Text('Value = $value');
}
return Center(child: CircularProgressIndicator());
},
)
If you want to use a where clause
await Firestore.instance.collection('collection_name').where(
FieldPath.documentId,
isEqualTo: "some_id"
).getDocuments().then((event) {
if (event.documents.isNotEmpty) {
Map<String, dynamic> documentData = event.documents.single.data; //if it is a single document
}
}).catchError((e) => print("error fetching data: $e"));
This is simple you can use a DOCUMENT SNAPSHOT
DocumentSnapshot variable = await Firestore.instance.collection('COLLECTION NAME').document('DOCUMENT ID').get();
You can access its data using variable.data['FEILD_NAME']
Update FirebaseFirestore 12/2021
StreamBuilder(
stream: FirebaseFirestore.instance
.collection('YOUR COLLECTION NAME')
.doc(id) //ID OF DOCUMENT
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return new CircularProgressIndicator();
}
var document = snapshot.data;
return new Text(document["name"]);
}
);
}
This is what worked for me in 2021
var userPhotos;
Future<void> getPhoto(id) async {
//query the user photo
await FirebaseFirestore.instance.collection("users").doc(id).snapshots().listen((event) {
setState(() {
userPhotos = event.get("photoUrl");
print(userPhotos);
});
});
}
Use this code when you just want to fetch a document from firestore collection , to perform some operations on it, and not to display it using some widget (updated jan 2022 )
fetchDoc() async {
// enter here the path , from where you want to fetch the doc
DocumentSnapshot pathData = await FirebaseFirestore.instance
.collection('ProfileData')
.doc(currentUser.uid)
.get();
if (pathData.exists) {
Map<String, dynamic>? fetchDoc = pathData.data() as Map<String, dynamic>?;
//Now use fetchDoc?['KEY_names'], to access the data from firestore, to perform operations , for eg
controllerName.text = fetchDoc?['userName']
// setState(() {}); // use only if needed
}
}
Simple way :
StreamBuilder(
stream: FirebaseFirestore.instance
.collection('YOUR COLLECTION NAME')
.doc(id) //ID OF DOCUMENT
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return new CircularProgressIndicator();
}
var document = snapshot.data;
return new Text(document["name"]);
}
);
}
var document = await FirebaseFirestore.instance.collection('Users').doc('CXvGTxT49NUoKi9gRt96ltvljz42').get();
Map<String,dynamic>? value = document.data();
print(value!['userId']);
You can get the Firestore document by following code:
future FirebaseDocument() async{
var variable = await FirebaseFirestore.instance.collection('Collection_name').doc('Document_Id').get();
print(variable['field_name']);
}
Use this simple code:
Firestore.instance.collection("users").document().setData({
"name":"Majeed Ahmed"
});

Flutter :Future builder doesn't gets the data

I wrote this code which queries Firebase database and returns some data. This thing was working totally fine till few days back but i don't know why now it returns "no data" even if i have data in the database
Is there a error in the code or Something else cause this ?
Fetching the data from Firebase
getsearchfood(String query) {
Future<QuerySnapshot> search =
Firestore.instance.collection("data").getDocuments();
setState(() {
searched = search;
});
}
Operating the data
buildsearchfood() {
return FutureBuilder(
future: searched,
builder: (context, snapshot) {
if (snapshot.hasData) {
print("no data");
}
else{
print("present data");
}
return Container(
);
},
);
}
Add await, as it is an asynchronous operation
Future<QuerySnapshot> search =
await Firestore.instance.collection("data").getDocuments();
You don't need to setState like you did.
Try this:
buildsearchfood() {
return FutureBuilder(
future: Firestore.instance.collection("data").getDocuments(),
builder: (context, snapshot) {
if (snapshot.hasData) {
// This means you have data. You can reference it in `snapshot.data`
}
else{
// This means NO data
}
},
);
}

Flutter StreamBuilder for multiple firebase documents

I am trying to make my Flutter app update when a change is made to the usersCollection.document(user.uid) firebase document.
When the user document is updated I want to retrieve the data from this document but also from another firebase document, facilitiesCollection.document(...).
My current code
Future<Map> _getCheckedInFacilityData() async {
Map<String, dynamic> result = {};
try {
DocumentSnapshot userDoc =
await _db.usersCollection.document(user.uid).get();
if (userDoc.data['checkedIn']) {
// User is checked in
DocumentSnapshot facDoc = await _db.facilitiesCollection
.document(userDoc.data['activeFacilityID'].toString())
.get();
result['facilityID'] = userDoc.data['activeFacilityID'];
result['sessionID'] = userDoc.data['activeSessionID'];
result['facilityActiveUsers'] = facDoc.data['activeUsers'].length;
result['facilityName'] = facDoc.data['name'];
return result;
}
} catch (er) {
debugPrint(er.toString());
}
return null;
}
FutureBuilder<Map>(
future: _getCheckedInFacilityData(),
builder: (context, map) {
switch (map.connectionState) {
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
...
This is currently working but the page is not updated when a change is made to the user document.
I haven't been using Flutter/Dart for long so any ideas are welcome.
Is it possible to return a custom object/map which is comprised of 2 separate documents from a StreamBuilder, or is there another method that will work in my situation.
Surely you can do it with Streams asyncMap() and then listen in StreamBuilder
Basic algoritm
Get stream of you first data type and then asyncMap to wait second data type and return them both
stream.asyncMap(
(v1) async {
final v2 = await Future.delayed(Duration(seconds: 1), () => 4);
return v1 * v2;
},
);
Closer to your code
Stream<Map<String, dynamic>> _getCheckedInFacilityData() {
return _db.usersCollection.document(user.uid).snapshots()
.asyncMap(
(userDoc) async {
final DocumentSnapshot facDoc =
await _db.facilitiesCollection
.document(userDoc.data['activeFacilityID'].toString())
.get();
final Map<String, dynamic> userMap = userDoc.data;
final Map<String, dynamic> facMap = facDoc.data;
return userMap..addAll(facMap);
},
);
}
In this function I merge two maps - be carefull if both maps have identical keys map will keep only last was added key in our case from addAll(facMap)
Last step is to show you streamed data on screen - use StreamBuilder
StreamBuilder<Map>(
stream: _getCheckedInFacilityData(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text('${snapshot.error}');
} else if (snapshot.connectionState == ConnectionState.waiting) {
return LinearProgressIndicator();
}
return /* some widget that shows your data*/;
},
),

Query a single document from Firestore in Flutter (cloud_firestore Plugin)

Edit: This Question is outdated, and I am sure, new documentation and more recent answers are available as of now.
I want to retrieve data of only a single document via its ID. My approach with example data of:
TESTID1 {
'name': 'example',
'data': 'sample data',
}
was something like this:
Firestore.instance.document('TESTID1').get() => then(function(document) {
print(document('name'));
}
but that does not seem to be correct syntax.
I was not able to find any detailed documentation on querying firestore within flutter (dart) since the firebase documentation only addresses Native WEB, iOS, Android etc. but not Flutter. The documentation of cloud_firestore is also way too short. There is only one example that shows how to query multiple documents into a stream which is not what i want to do.
Related issue on missing documentation:
https://github.com/flutter/flutter/issues/14324
It can't be that hard to get data from a single document.
UPDATE:
Firestore.instance.collection('COLLECTION').document('ID')
.get().then((DocumentSnapshot) =>
print(DocumentSnapshot.data['key'].toString());
);
is not executed.
but that does not seem to be correct syntax.
It is not the correct syntax because you are missing a collection() call. You cannot call document() directly on your Firestore.instance. To solve this, you should use something like this:
var document = await Firestore.instance.collection('COLLECTION_NAME').document('TESTID1');
document.get() => then(function(document) {
print(document("name"));
});
Or in more simpler way:
var document = await Firestore.instance.document('COLLECTION_NAME/TESTID1');
document.get() => then(function(document) {
print(document("name"));
});
If you want to get data in realtime, please use the following code:
Widget build(BuildContext context) {
return new StreamBuilder(
stream: Firestore.instance.collection('COLLECTION_NAME').document('TESTID1').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return new Text("Loading");
}
var userDocument = snapshot.data;
return new Text(userDocument["name"]);
}
);
}
It will help you set also the name to a text view.
Null safe code (Recommended)
You can either query the document in a function (for example on press of a button) or inside a widget (like a FutureBuilder).
In a method: (one time listen)
var collection = FirebaseFirestore.instance.collection('users');
var docSnapshot = await collection.doc('doc_id').get();
if (docSnapshot.exists) {
Map<String, dynamic>? data = docSnapshot.data();
var value = data?['some_field']; // <-- The value you want to retrieve.
// Call setState if needed.
}
In a FutureBuilder (one time listen)
FutureBuilder<DocumentSnapshot<Map<String, dynamic>>>(
future: collection.doc('doc_id').get(),
builder: (_, snapshot) {
if (snapshot.hasError) return Text ('Error = ${snapshot.error}');
if (snapshot.hasData) {
var data = snapshot.data!.data();
var value = data!['some_field']; // <-- Your value
return Text('Value = $value');
}
return Center(child: CircularProgressIndicator());
},
)
In a StreamBuilder: (always listening)
StreamBuilder<DocumentSnapshot<Map<String, dynamic>>>(
stream: collection.doc('doc_id').snapshots(),
builder: (_, snapshot) {
if (snapshot.hasError) return Text('Error = ${snapshot.error}');
if (snapshot.hasData) {
var output = snapshot.data!.data();
var value = output!['some_field']; // <-- Your value
return Text('Value = $value');
}
return Center(child: CircularProgressIndicator());
},
)
If you want to use a where clause
await Firestore.instance.collection('collection_name').where(
FieldPath.documentId,
isEqualTo: "some_id"
).getDocuments().then((event) {
if (event.documents.isNotEmpty) {
Map<String, dynamic> documentData = event.documents.single.data; //if it is a single document
}
}).catchError((e) => print("error fetching data: $e"));
This is simple you can use a DOCUMENT SNAPSHOT
DocumentSnapshot variable = await Firestore.instance.collection('COLLECTION NAME').document('DOCUMENT ID').get();
You can access its data using variable.data['FEILD_NAME']
Update FirebaseFirestore 12/2021
StreamBuilder(
stream: FirebaseFirestore.instance
.collection('YOUR COLLECTION NAME')
.doc(id) //ID OF DOCUMENT
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return new CircularProgressIndicator();
}
var document = snapshot.data;
return new Text(document["name"]);
}
);
}
This is what worked for me in 2021
var userPhotos;
Future<void> getPhoto(id) async {
//query the user photo
await FirebaseFirestore.instance.collection("users").doc(id).snapshots().listen((event) {
setState(() {
userPhotos = event.get("photoUrl");
print(userPhotos);
});
});
}
Use this code when you just want to fetch a document from firestore collection , to perform some operations on it, and not to display it using some widget (updated jan 2022 )
fetchDoc() async {
// enter here the path , from where you want to fetch the doc
DocumentSnapshot pathData = await FirebaseFirestore.instance
.collection('ProfileData')
.doc(currentUser.uid)
.get();
if (pathData.exists) {
Map<String, dynamic>? fetchDoc = pathData.data() as Map<String, dynamic>?;
//Now use fetchDoc?['KEY_names'], to access the data from firestore, to perform operations , for eg
controllerName.text = fetchDoc?['userName']
// setState(() {}); // use only if needed
}
}
Simple way :
StreamBuilder(
stream: FirebaseFirestore.instance
.collection('YOUR COLLECTION NAME')
.doc(id) //ID OF DOCUMENT
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return new CircularProgressIndicator();
}
var document = snapshot.data;
return new Text(document["name"]);
}
);
}
var document = await FirebaseFirestore.instance.collection('Users').doc('CXvGTxT49NUoKi9gRt96ltvljz42').get();
Map<String,dynamic>? value = document.data();
print(value!['userId']);
You can get the Firestore document by following code:
future FirebaseDocument() async{
var variable = await FirebaseFirestore.instance.collection('Collection_name').doc('Document_Id').get();
print(variable['field_name']);
}
Use this simple code:
Firestore.instance.collection("users").document().setData({
"name":"Majeed Ahmed"
});

Resources