Getting One-Time Read Data From Nested Firestore in Flutter - firebase

I'm currently trying to make an app that need to read data just for one time from a nested Firestore Database.
Here's my currently working code
FirebaseFirestore firestore = FirebaseFirestore.instance;
CollectionReference users = firestore.collection('users');
FirebaseAuth _auth = FirebaseAuth.instance;
FutureBuilder getTheData = FutureBuilder<DocumentSnapshot>(
future: users.doc(_auth.currentUser!.uid).get(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError || (snapshot.hasData && !snapshot.data!.exists)) {
return Text("Something went wrong");
}
if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data =
snapshot.data!.data() as Map<String, dynamic>;
String _condition = data['condition'];
String _descCondition = data['descCondition'];
int _percentageValue = data['percentageValue'];
return defaultDraw(
condition: _condition,
descCondition: _descCondition,
percentageValue: _percentageValue,
);
}
return defaultDraw(
condition: "Loading",
descCondition: "",
percentageValue: 0,
);
},
);
and it's able to read this data (condition, descCondition, and percentageValue) :
but when I'm trying to read this data :
Which is another collection inside of the previous document (a collection named 'adviceData' with random document ID that I'm trying to read), I'm getting an error like this :
Here's my code :
FirebaseFirestore firestore = FirebaseFirestore.instance;
CollectionReference users = firestore.collection('users');
FirebaseAuth _auth = FirebaseAuth.instance;
FutureBuilder _getParameterData = FutureBuilder<DocumentSnapshot>(
future: users
.doc(_auth.currentUser!.uid)
.collection('adviceData')
.doc()
.get(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError || (snapshot.hasData && !snapshot.data!.exists)) {
return Text("Something went wrong");
}
if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data =
snapshot.data!.data() as Map<String, dynamic>;
String _advicePhLow = data['advicePhLow'];
String _advicePhHigh = data['advicePhHigh'];
String _adviceTempLow = data['adviceTempLow'];
String _adviceTempHigh = data['adviceTempHigh'];
String _adviceHeightLow = data['adviceHeightLow'];
String _adviceHeightHigh = data['adviceHeightHigh'];
return drawParameter(
advicePhLow: _advicePhLow,
adviceTempLow: _adviceTempLow,
adviceHeightLow: _adviceHeightLow,
);
}
return drawParameter();
},
);
and my question is :
Where did I make mistakes?
Is there any better method to get a data from firestore?
Thanks in advance

If you want to get all documents from the subcollection adviceData you would need to get a QuerySnapshot like here:
FutureBuilder<QuerySnapshot>(
future: users
.doc(_auth.currentUser!.uid)
.collection('adviceData')
.get(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return ListView(
children: snapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data = document.data()! as Map<String, dynamic>;
return ListTile(
title: Text(data['full_name']),
subtitle: Text(data['company']),
);
}).toList(),
);
},
);
}
If you want to get a single document from that subcollection you would need to define the id of that specific document:
FutureBuilder _getParameterData = FutureBuilder<DocumentSnapshot>(
future: users
.doc(_auth.currentUser!.uid)
.collection('adviceData')
.doc('documentID') // here
.get(),
builder:
You can find more about both methods here.

Related

Is there anyway that I can check the Documents Snapshot FirebaseFireStore flutter

I m trying to snapshot the Collection of users and I want to check if the user has the field or not
If the users has then show this
If user doesn’t have then show this
This is the code i got so far.
class _HalfScreenState extends State<HalfScreen> {
final userUid = FirebaseAuth.instance.currentUser!.uid;
#override
Widget build(BuildContext context) {
return StreamBuilder<DocumentSnapshot?>(
stream: FirebaseFirestore.instance
.collection("users")
.doc(userUid)
.snapshots(),
builder: (context, snapshot) {
if (snapshot.data != null) {
return const Text("Loading...");
}
return Text(
(snapshot.data as DocumentSnapshot)['groupId'],
),
});
}
}
Step by step that'd be:
Get the data from the document as a Map.
Check if the map contains a key for the field you're looking for.
In code that'd be something like this:
// πŸ‘‡ Indicate the type of the data in the document
return StreamBuilder<DocumentSnapshot<Map<String, dynamic>>>(
stream: FirebaseFirestore.instance
.collection("users")
.doc(userUid)
.snapshots(),
builder: (context, snapshot) {
if (snapshot.data != null) {
return const Text("Loading...");
}
// πŸ‘‡ Get the data (Map<String, dynamic>
var data = snapshot.data.data();
return Text(
// πŸ‘‡ Check if the field is tere
data.containsKey('groupId') ? 'Yes, it's there' : 'Nope, field not found'
),
}
);

How to retreive data from firestore flutter

I'm new into flutter and firebase integrations and I'm having some troubles to retreive all the data from the firebase collection.
I have tried this method:
getCollection() {
CollectionReference coleccion =
FirebaseFirestore.instance.collection('materias');
return Container(
child: StreamBuilder(
stream: coleccion.doc('aprobadas').snapshots(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
return Text(snapshot.data.data()['codigo'],
style: TextStyle(fontSize: 50, color: Colors.white));
} else {
return CircularProgressIndicator();
}
},
),
);
}
Now I'm a little bit frustrated because I have tried a differents methods and doesn't work.
I really appreciate all the help.
Best regards
Data can be retrieved using the below code from firestore to flutter.
One-time Read
call the Query.get or DocumentReference.get methods
class GetUserName extends StatelessWidget {
final String documentId;
GetUserName(this.documentId);
#override
Widget build(BuildContext context) {
CollectionReference users = FirebaseFirestore.instance.collection('users');
return FutureBuilder<DocumentSnapshot>(
future: users.doc(documentId).get(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return Text("Something went wrong");
}
if (snapshot.hasData && !snapshot.data!.exists) {
return Text("Document does not exist");
}
if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data = snapshot.data!.data() as Map<String, dynamic>;
return Text("Full Name: ${data['full_name']} ${data['last_name']}");
}
return Text("loading");
},
);
}
}
Realtime changes
FlutterFire provides support for dealing with real-time changes to collections and documents. A new event is provided on the initial request, and any subsequent changes to collection/document whenever a change occurs (modification, deleted or added).
Both the CollectionReference & DocumentReference provide a snapshots() method which returns a Stream:
Stream collectionStream = FirebaseFirestore.instance.collection('users').snapshots();
Stream documentStream = FirebaseFirestore.instance.collection('users').doc('ABC123').snapshots();
Please refer official documentation here
You can use a StreamBuilder. That will be easy to understand.
StreamBuilder(
stream: FirebaseFirestore.instance.collection("collection").snapshot,
builder: (BuildContext context,snapshot) {
if(snapshot.hasdata!=true) {
return CircularProgressIndicator();
} else {
return ListView.builder(
itemcount:snapshot.data.docs.length,
builder(context,index) {
return Text(snapshot.data.docs[index].data()["filedname"]);
}
}
)

How to properly access a collection's data to be used for a list in flutter with Firestore

So I need to get access to a piece of data called groupId which will be used as a doc path for a collection called Members that I will be used to retrieve data from and put in a list. The problem is that it requires an async which I don't know where to put as I get errors. Here's the code:
#override
Widget build(BuildContext context) {
final CollectionReference users = firestore.collection('UserNames');
final String uid = auth.currentUser.uid;
final result = await users.doc(uid).get(); //This await requires an async but I don't how to do that
final groupId = result.data()['groupId'];
// <1> Use FutureBuilder
return FutureBuilder<QuerySnapshot>(
// <2> Pass `Future<QuerySnapshot>` to future
future: FirebaseFirestore.instance.collection('Groups').doc(groupId).collection('Members').get(), //Once the async problem is solved i will be able to save the groupId as. variable to be used in my doc path to access this collection. How do I do this?
builder: (context, snapshot) {
if (snapshot.hasData) {
// <3> Retrieve `List<DocumentSnapshot>` from snapshot
final List<DocumentSnapshot> documents = snapshot.data.docs;
return ListView(
children: documents
.map((doc) => Card(
child: ListTile(
title: Text(doc['displayName']),
subtitle: Text(doc['plastics'].toString()),
),
))
.toList());
} else if (snapshot.hasError) {
return Text('Its Error!');
}
});
}
Wrap your FutureBuilder in another FutureBuilder.
users.doc(uid).get() returns a Future. If you are using it in a widget, you use FutureBuilder.
await and futureBuilder do the similar things
#override
Widget build(BuildContext context) {
final CollectionReference users = firestore.collection('UserNames');
final String uid = auth.currentUser.uid;
return FutureBuilder(
future: users.doc(uid).get(),
builder: (context, snapshot) {
if (snapshot.hasData) {
final result = snapshot.data;
final groupId = result.data()['groupId'];
return FutureBuilder<QuerySnapshot>(
// <2> Pass `Future<QuerySnapshot>` to future
future: FirebaseFirestore.instance
.collection('Groups')
.doc(groupId)
.collection('Members')
.get(), //Once the async problem is solved i will be able to save the groupId as. variable to be used in my doc path to access this collection. How do I do this?
builder: (context, snapshot) {
if (snapshot.hasData) {
// <3> Retrieve `List<DocumentSnapshot>` from snapshot
final List<DocumentSnapshot> documents = snapshot.data.docs;
return ListView(
children: documents
.map((doc) => Card(
child: ListTile(
title: Text(doc['displayName']),
subtitle: Text(doc['plastics'].toString()),
),
))
.toList());
} else if (snapshot.hasError) {
return Text('Its Error!');
}
});
}
});
}

How can I put retrieved data from firebase to a widget in Flutter?

final String uid;
final firestoreInstance = Firestore.instance;
void printData() async{
var firebaseUser = await FirebaseAuth.instance.currentUser();
firestoreInstance.collection("users").document(firebaseUser.uid).get().then((value){
print(value.data);
});
}
So this function prints to console the retrieved data from firebase firestore when I press the button. But I want to put them into a widget and display them in my app. For ex: Text(printData()) - but it's not allowed. How can I do that?
You need to use a FutureBuilder to display data that you are getting asynchronously, therefore create a method that returns a Future:
Future<DocumentSnapshot> getDocument() async{
var firebaseUser = await FirebaseAuth.instance.currentUser();
return Firestore.instance.collection("users").document(firebaseUser.uid).get();
}
Then in the FutureBuilder assign the method to the property future:
body: Container(
padding: EdgeInsets.all(10.0),
child: FutureBuilder(
future: getDocument(),
builder: (context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Text(snapshot.data["name"].toString());
} else if (snapshot.connectionState == ConnectionState.none) {
return Text("No data");
}
return CircularProgressIndicator();
},
),
Assuming you have the following db:
Collection ("users") ----> Document (userId) ---> fields name : your_name
Read more about future:
https://api.dart.dev/stable/2.9.1/dart-async/Future-class.html
https://dart.dev/codelabs/async-await
https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html

Flutter: Firestore Get User uid inside StreamBuilder

I have an app which I want to display documents inside collection.. the collection reference is the uid of the user.
Is there a way to get current user uid and put this uid inside StreamBuilder in stream.
I have tried like so but it did not work and returned null:
class _MyAdsState extends State<MyAds> {
final FirebaseAuth _auth = FirebaseAuth.instance;
Future getCurrentUser() async {
final FirebaseUser user = await _auth.currentUser();
final uid = user.uid;
print(uid);
return uid.toString();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
Expanded(
child: StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection("${getCurrentUser()}").snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> querySnapShot){
if(querySnapShot.hasError){
return Text('Some Error');
}
if(querySnapShot.connectionState == ConnectionState.waiting){
return CircularProgressIndicator();
}else{
final list = querySnapShot.data.documents;
return ListView.builder(
itemBuilder: (context, index){
return ListTile(
title: Text(list[index]["subject"]),
subtitle: Text(list[index]["category"]),
);
},
itemCount: list.length,
);
}
},
)
Getting the UID is an asynchronous operation, so requires a FutureBuilder.
If you want to use the UID to then build a stream, you'll need to have a FutureBuilder for the UID, and then inside of that a StreamBuilder for the stream from the database.
body: FutureBuilder(
future: FirebaseAuth.instance.currentUser(),
builder: (context, AsyncSnapshot<FirebaseUser> snapshot) {
if (snapshot.hasData) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection(snapshot.data.uid).snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> querySnapShot){
...
},
)
}
else {
return Text('Loading user data...');
}
THANK YOU GUYS!
I was looking for this for too long now. I had the "problem" that I was recording the senderUID for a sent message only, but of course wanted the Name being displayed in the "sentFrom" field. So I had to query Firestore for the UID and pull out the email. My solution:
FutureBuilder<QuerySnapshot>(
future: _firestore.collection("users").get(),
builder: (context, futureSnapshot) {
if (!futureSnapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
Map<String, String> users = {};
final userData = futureSnapshot.data.docs;
for (var user in userData) {
users[user.id] = user.data()["email"];
}
return StreamBuilder<QuerySnapshot>(
stream: _firestore.collection("messages").snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
// ignore: missing_return
final messages = snapshot.data.docs;
List<Widget> messageWidgets = [];
for (var message in messages) {
final messageText = message.data()["text"];
final messageEmail = users[message.data()["senderUID"]];
messageWidgets
.add(Text("$messageText from $messageEmail"));
}
return Column(children: messageWidgets);
},
);
},
),
I just created a map from the data and used it inside the stream builder. Is there maybe a better solution?

Resources