How do I fetch sub collection from firestore using flutter? - firebase

I wanna fetch the data inside the Guests like guestName. The code below I used it to fetch collection and it worked, but I have no idea how to fetch subcollection. I been trying but it's not working.
body: CustomScrollView(
slivers: [
SliverPersistentHeader(pinned: true, delegate: SearchBoxDelegate()),
StreamBuilder<QuerySnapshot>(
//I used this to fetch collection but it doesn't work with subcollection
stream: Firestore.instance
.collection("Guests")
.orderBy("Date", descending: true).snapshots(),
builder: (context, dataSnapshot) {
return !dataSnapshot.hasData
? SliverToBoxAdapter(
child: Center(
child: circularProgress(),
),
)
: SliverStaggeredGrid.countBuilder(
crossAxisCount: 1,
staggeredTileBuilder: (c) => StaggeredTile.fit(1),
itemBuilder: (context, index) {
ItemModel model = ItemModel.fromJson(
dataSnapshot.data.documents[index].data);
return sourceInfo(model, context);
},
itemCount: dataSnapshot.data.documents.length,
);
},
),
],
),
Container(
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
child: Text(
// this guestName i wanna fetch
model.guestName,
style: TextStyle(
color: Colors.black, fontSize: 14.0),
),
),
],
),
),

To get a CollectionReference for a subcollection, you first need to have a DocumentReference object to its parent document, and then you can call collection() on that.
In your builder you have dataSnapshot.data.documents[index], which gives you a DocumentSnapshot object, from which you can get a DocumentReference by calling reference on it.
So combined that'd be something like this in your builder:
dataSnapshot.data.documents[index].reference.collection("Guests")
I recommend always keeping the reference documentation handy that I linked above, as it's the easiest way to find this type of API path.

Related

Firebase doesn't work cause of null-safety (DART/FLUTTER)

I'm using/learning Firebase for my database works. My snapshot's coming like _jsonQuerySnapshot or _jsonDocumentSnapshot. But it had to be QuerySnapshot or DocumentSnapshot. Because of this I have to encode and decode my snapshot for use my datas.
If I'm not using encode decode json I'm getting null or object errors all the time.
Here is my class extends from state
class _MyHomePageState extends State<MyHomePage> {
final _firestore = FirebaseFirestore.instance;
#override
Widget build(BuildContext context) {
CollectionReference moviesRef=_firestore.collection('movies');
DocumentReference babaRef = _firestore.collection('movies').doc('Baba');
return Scaffold(
backgroundColor: Colors.grey,
appBar: AppBar(
title: Text('FireStore Crud'),
),
body: Center(
child: Container(
child: Column(
children: [
StreamBuilder<QuerySnapshot>(
stream: moviesRef.snapshots(),
builder: (BuildContext context,AsyncSnapshot asyncSnapshot){
List<DocumentSnapshot>listOfDocumentSnapshot=asyncSnapshot.data.docs;
return Flexible(
child: ListView.builder(
itemCount: listOfDocumentSnapshot.length,
itemBuilder: (context,index){
Text('${listOfDocumentSnapshot[index].data()['name']}' ,style: TextStyle(fontSize: 24),);
},
),
);
},
),
],
),
),
),
);
}
}
and this is my error .
First of all, check your data is null or not and then use [] on it. Probably, listOfDocumentSnapshot[index].data() is null. If it is null, render another UI such as loading screen. Namely, your loading screen must be showed until reach the data.
for example:
builder: (BuildContext context,AsyncSnapshot asyncSnapshot){
List<DocumentSnapshot>? listOfDocumentSnapshot = asyncSnapshot.data.docs;
if(!listOfDocumentSnapshot.hasData || listOfDocumentSnapshot == null){
return LoadingScreen(); //etc.
}
return Flexible(
child: ListView.builder(
itemCount: listOfDocumentSnapshot.length,
itemBuilder: (context,index){
Text('${listOfDocumentSnapshot[index].data()['name']}' ,style: TextStyle(fontSize: 24),);
},
),
);
},
Futures (asynchronous programmes) need some time to get data and you have to make your UI wait until you get your data. e.g. database connections, read/write somethings to/from somewhere etc.
For more detail you can read this article.

How to show data in Flutter from Firestore collection based on the value in the array?

I need to remove documents - reviews/posts - from the stream, which the user has hidden. When a user decides to hide the post, the post collection's post-doc creates an array 'hidingUserId' of userIDs that has hidden it. refer to the pic, pls.
now I need to remove the post where the array 'hidingUserId' contains the currentUserId. I have tried the following, but it does not display errors in the log and it does not display the posts. Just circular progress circling on the screen.
Stream streams = postRef
.orderBy('stars', descending: false)
.orderBy('timestamp', descending: true)
.snapshots();
#override
Widget build(BuildContext context) {
return ListView(
shrinkWrap: true,
padding: EdgeInsets.symmetric(horizontal: 10.0),
children: [
StreamBuilderWrapper(
shrinkWrap: true,
stream: streams.where((event) => event.data()['hidingUserId']
== null
|| event.data()['hidingUserId'].contains(!currentUserId()) ?
streams.forEach((event) => print(event))
: Container(
child: Center(
child: Text('Nothing to show'),))),
physics: NeverScrollableScrollPhysics(),
itemBuilder: (_, DocumentSnapshot snapshot) {
internetChecker();
Review posts = Review.fromJson(snapshot.data());
return posts.postId != null
? Padding(
padding: const EdgeInsets.only(bottom: 12.0),
child: Posts(post: posts),
)
: Container(
child: Center(
child: Text('Nothing to show'),
),
);
},
),
],
);
}
am new to Firestore, and I can't find the information I need. help appreciated very much! Thanks!

Flutter / Firestore - How can i fetch collection in subcollection userid equal to currentuser

My Firestore structure here,
i want to fetch lesson data.But only users with equal user ids in the member collection.But i can't.
is that worst usage?
body: StreamBuilder(
stream: firestore
.collection('lessons')
.where('members', isEqualTo: FirebaseAuth.instance.currentUser.uid)
.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
} else {
return Container(
child: ListView(
scrollDirection: Axis.vertical,
children: snapshot.data.docs.map((document) {
return Card(
margin: EdgeInsets.all(5),
child: Column(
children: [
ListTile(
title: Text(document['lessonTitle']),
leading: Icon(Icons.label),
trailing: Text(document['lessonSubtitle']),
)
],
),
);
}).toList(),
),
);
}
},
),
There is no way to select documents from one collection based on values in another (sub)collection. Queries in Firestore can only consider information in the documents that they're returning.
If you want to select lessons based on their members, you'll need to include that information in each lesson document itself. The common way to do this is to add a members array to each lesson document, use arrayUnion and arrayRemove operations to manipulate that array, and then use array-contains to query the array.

Flutter/Firestore: Displaying gridtiles in categories

We are creating a recipe app for a school project.
We are using Dart/Flutter for the language and we have recipes stored in a Firestore DB collection called 'recipes' which have sub-collections of ingredients, comments and method. Inside the ingredients collection, there is a field called 'proteins' that contains an array of proteins (beef, pork, poultry, etc)
I have managed to make one big grid view which displays thumbnails of all the recipes currently stored in the DB, but we want to set them in categories by their proteins. I managed to make the individual lists for the categories but they each contain all of the recipes. I don't know now which direction to go to somehow search through the DB and then display them on the page.
This is the code for the current list that is being created.
I thought about somehow creating a search function that would create an array of document ID's which would be then used in the compiling of the lists, but not sure where to start
I'm just trying to get some nudge in the direction of how it would be done and not the code. The process of it if you will.
Thanks in advance
child: Container(
alignment: Alignment.bottomRight,
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height - 100.0,
child: ListView.builder(
itemCount: 4,
itemBuilder: (context, index) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 8.0),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'${categories[index]}',//displays the protein name (beef
style: TextStyle(
color: Colors.red,
fontWeight: FontWeight.bold,
fontSize: 25.0),
),
),
),
Container(
height: 180.0,
child: StreamBuilder(
stream: firestoreDb,
builder: (
context,
snapshot,
) {
if (!snapshot.hasData)
return CircularProgressIndicator();
return ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: snapshot.data.docs.length,
itemBuilder: (context, int index) {
return GestureDetector(
child: Card(
elevation: 5,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(10)),
child: TestGridTile(
snapshot: snapshot.data,
index: index,
),
),
);
});
}),
),
],
),
),
)),
EDIT TestGridTile code as requested
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:youth_food_movement/recipe/ui/ingredients_page.dart';
//card that displays the recipe information
class TestGridTile extends StatelessWidget {
//snapshot of the database
final QuerySnapshot snapshot;
final int index;
const TestGridTile({Key key, this.snapshot, this.index}) : super(key: key);
static String idNumber;
#override
Widget build(BuildContext context) {
//snaphot of the docs
// ignore: unused_local_variable
var snapshotData = snapshot.docs[index];
var docID = snapshot.docs[index].id;
String recipeID = docID.toString();
return Container(
width: 150,//MediaQuery.of(context).size.width,
height: 150,//MediaQuery.of(context).size.height * 0.25,
//get the image URL
child: FutureBuilder(
future: _getImageURL(docID),
builder: (context, snapshot) {
if (snapshot.hasData) {
//return the image and make it cover the container
return GestureDetector(
child: Image.network(
snapshot.data,
fit: BoxFit.fill,
),
onTap: () {
idNumber = recipeID;
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) =>
IngredientsPage(recipeID)));
},
);
} else {
return Container(
child: Center(
child: CircularProgressIndicator(),
));
}
}),
);
}
//method to get the image URL
Future _getImageURL(var docID) async {
//declare and instantiate the firebase storage bucket
final FirebaseStorage storage = FirebaseStorage.instanceFor(
bucket: 'gs://youth-food-movement.appspot.com');
//ref string will change so the parameter will be the jpg ID (maybe)
String downloadURL =
await storage.ref('recipe_images/$docID').getDownloadURL();
return downloadURL;
}
}```
I have attached an image of how it looks currently[![current image][1]][1]
[1]: https://i.stack.imgur.com/HiaYi.jpg
Because the ingredience is a collection we don't get it with the with the initial data. You have two options:
As it is now load all recipes and for each of them the ingrediences and for each category filter out the recipes that don't fit in it and show only those that do fit in. If you would share more code I could help you with it. What is behind TestGridTile.
Load for each category only the recipes that fin it by using a query with where clause and the arrayContaines. But for that the array proteins can't be nested in the ingredience subcollection of the collection you are filtering.
Both of the would be much easier if you would move the proteines array to the doc of the recipes collection. You could do that with the client side code or even with Firebase Cloud Functions.

Get information from Firebase and able to use it with a Gridtile and GestureDetector in Flutter

So I'm trying to design a Widget which can basically create a view where you can have something like this:
The problem that I'm facing is that for this example I used is calls to API's of Firebase, however, now I'm using the Firebase Auth and Firestore Extentionts and don't want to use the provider package or HTTP request, I have the following Firestore Collection:
As you see the collection name is users but inside has the user uid given by Firebase as a Document and inside that document has all the user Data.
So I need some help to have the following functionalities:
I need to create a ListView preferably ListView Builder so it can show the entire collection so it can display all the users contained in the collection. (Each user is a Document titled with his own User uid).
In the Tile, I want to display just name and Image but once it is clickable it should send me to a new page (the user profile page sending as an argument the uid)
I got this code: but I don't have the logic on how to get the list tile clickable and get the uid of the click user (in the tile) to send the info (uid) to the next page. But once I know how to display the contents of all the documents in the collection (displaying the name (nombre) inside each document as a title and how to capture the uid, I bet I can do the modifications and eliminations.
import 'package:flutter/material.dart';
import 'package:firebase_firestore/firebase_firestore.dart';
class ExpenseList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection("users").snapshots,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) return new Text("No existe registros de Usuario");
return new ListView(children: getExpenseItems(snapshot));
});
}
getExpenseItems(AsyncSnapshot<QuerySnapshot> snapshot) {
return snapshot.data.documents
.map((doc) => ListTile(title: new Text(doc["nombre"], onTap: (){
//Here function to navigate to profile page saving the uid
}))
.toList();
}
}
In that list tile, I should have a Network image as I can have a parameter the image from the document in the collection.
Is there anything I can do?
Kind Regards.
Here you can find an example of a custom build ListView.
var futureBuilder=new FutureBuilder(
future: makecall.firebaseCalls(databaseReference), // async work
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none: return new Text('Press button to start');
case ConnectionState.waiting: return new Text('Loading....');
default:
if (snapshot.hasError)
return new Text('Error: ${snapshot.error}');
else
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index){
return Card(
elevation: 0.0,
child: Padding(
padding: const EdgeInsets.all(0.0),
child: SizedBox(
height: MediaQuery.of(context).size.height*0.15,
width: MediaQuery.of(context).size.width,
child: Card(
elevation: 0,
child: Row(
children: <Widget>[
new Container(
child: Image.network(snapshot.data[index].iconUrl,height: MediaQuery.of(context).size.width*0.3,width: MediaQuery.of(context).size.width*0.3,),
),
Padding(
padding: const EdgeInsets.only(left: 10,right: 5,top: 5),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
children: <Widget>[
condition('${snapshot.data[index].vegOrNon}')==true? new Image.asset('images/non_veg.png',height: 15,width: 15,): new Image.asset('images/veg.jpg',height: 15,width: 15),
SizedBox(width: 5),
Text(snapshot.data[index].foodtitle, style: TextStyle(fontWeight: FontWeight.w500, fontSize: 20,fontFamily: 'Roboto-Black'),)
],
),
SizedBox(height:10.0),
Row(
children: <Widget>[
new IconTheme(
data: new IconThemeData(
color: Colors.black26),
child: new Icon(Icons.timer,size: 20.0,),
),
Text('${snapshot.data[index].PreptimeInMins} minutes',style: TextStyle(fontWeight: FontWeight.w700,color: Colors.black26),),
],
),
],
)
],
),
),
],
),
),
],
)
)
),
),
);
},
);
}
},
);
I found this example on this guide and I think that it can be usefull for you.
If your issue is how to make a ListView tile clickable you can use the GestureDetector class to wrap your tile and send the UID in the .OnTap event

Resources