displaying data from different firestore collections - flutter

I'm attempting display data from two diffrent collections within firestore , I treied to nest both streambuilds so i can particulary display the data as one stream , however I keep on getting the error bad state field doesnt exist with doc snapshot how can i fixing thus error , or is there another much more effective method i can use to display data from two diffrent collections in one class?
below is screenshot of the data(s) i want to display:
class OrderStream extends StatelessWidget {
static const String route = "/Order";
final CollectionReference meal =
FirebaseFirestore.instance.collection("menu");
final CollectionReference profile =
FirebaseFirestore.instance.collection("users");
OrderStream({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder(
stream: profile.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> streamSnapshot) {
return StreamBuilder(
stream: meal.snapshots(),
builder:
(context, AsyncSnapshot<QuerySnapshot> streamSnapshot) {
if (!streamSnapshot.hasData) {
return const SizedBox(
height: 250,
child: Center(
child: CircularProgressIndicator(),
),
);
} else {
return ListView.builder(
itemCount: streamSnapshot.data!.docs.length,
itemBuilder: (context, index) {
final DocumentSnapshot documentSnapshot =
streamSnapshot.data!.docs[index];
return Column(
children: [
Text( documentSnapshot['price'],)
Text( documentSnapshot['name'],)
]
),
),
}

This is probably happening due to similar name for both snapshots.
The best way to check this is by renaming the snapshot for individual Streambuilder().
StreamBuilder(
stream: profile.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> profileStreamSnapshot) {
return StreamBuilder(
stream: meal.snapshots(),
builder:
(context, AsyncSnapshot<QuerySnapshot> mealStreamSnapshot) {
if (!streamSnapshot.hasData) {
//modified (renamed snapshot variable) code here
}

You can merge those two streams into 1 using library like rxdart which has combineLatest2 method although you can also use something like StreamZip to get the same effect.
I have used rxdart combineLatest2 as follows:
import 'package:rxdart/rxdart.dart';//import ⇐
class MyHomePage extends StatelessWidget {
final CollectionReference profile =
FirebaseFirestore.instance.collection("users");
final CollectionReference meal =
FirebaseFirestore.instance.collection("menu");
MyHomePage({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder(
stream: Rx.combineLatest2(profile.snapshots(), meal.snapshots(),
(QuerySnapshot profileSnapshot, QuerySnapshot mealSnapshot) {
return [...profileSnapshot.docs, ...mealSnapshot.docs];
}),
builder: (context, AsyncSnapshot<List<DocumentSnapshot>> snapshot) {
if (!snapshot.hasData) {
return const SizedBox(
height: 250,
child: Center(
child: CircularProgressIndicator(),
),
);
} else {
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
final DocumentSnapshot documentSnapshot =
snapshot.data![index];
final Map<String, dynamic> data =
documentSnapshot.data() as Map<String, dynamic>;
if (data.containsKey("price") && data.containsKey("name")) {
return Column(
children: [Text(data["price"]), Text(data["name"])],
);
} else {
return Container();
}
},
);
}
}),
);
}
}
You can also use Stream.merge() as follows:
final Stream<QuerySnapshot> mealsStream = meal.snapshots();
final Stream<QuerySnapshot> profilesStream = profile.snapshots();
//.. All that Scaffold stuff
stream: Stream.merge([mealsStream, profilesStream]),

Related

displaying current logged user data

i need assistance on how i could possibly query a current logged in / signed in users data so i can display their specific data using the snippet of code below -
class AddStore extends StatelessWidget {
AddStore({Key? key}) : super(key: key);
final CollectionReference _user =
FirebaseFirestore.instance.collection("users");
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder(
stream: _user.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> streamSnapshot) {
if (!streamSnapshot.hasData) {
return const SizedBox(
height: 250,
child: Center(
child: CircularProgressIndicator(),
),
);
} else {
return ListView.builder(
itemCount: streamSnapshot.data!.docs.length,
itemBuilder: ((context, index) {
final DocumentSnapshot documentSnapshot =
streamSnapshot.data!.docs[index];
return Column(
children: [
Text(documentSnapshot['fullName']),
],
);
}));
}
}));
}
}
You need to add the current user id i.e FirebaseAuth.instance.currentUser!.uid while querying the data from users collection. And this results in single document so you should avoid using ListView
Change the StreamBuilder to this
StreamBuilder<DocumentSnapshot<Map<String, dynamic>>>(
stream: FirebaseFirestore.instance
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid).snapshots(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return const Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text("Loading");
}
Map<String, dynamic> data =
snapshot.data!.data()! as Map<String, dynamic>;
return Text(data['fullName']);
},
)

Getting Null Value while Using Stream in Flutter

I was to trying to get an images from my firestore database. I used streambuilder to get images but it is showing me an error null value received. I checked the code completely and it was perfect. I dont know where the problem exist. Kindly help. Thanks.
Error
Null check operator used on a null value.
The following _CastError was thrown building StreamBuilder<QuerySnapshot<Map<String, dynamic>>>(dirty, state: _StreamBuilderBaseState<QuerySnapshot<Map<String, dynamic>>, AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>>>#b7a15):
GetImage Code
class GetImage extends StatelessWidget {
const GetImage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: FirebaseFirestore.instance
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.collection('cart')
.snapshots(),
builder: (context,
AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>> snapshot) {
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
return Container(
child: Image.network(
snapshot.data!.docs[index].data()['imageUrl'],
fit: BoxFit.cover,
alignment: Alignment.center,
),
);
});
});
}
}
The error is showing because you have not provided the code for the loading state of a streambuilder, when the data is being loaded. Here is the updated code.
class GetImage extends StatelessWidget {
const GetImage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: FirebaseFirestore.instance
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.collection('cart')
.snapshots(),
builder: (context,
AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(), // or the data you want to show while loading...
);
}
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
return Container(
child: Image.network(
snapshot.data!.docs[index].data()['imageUrl'],
fit: BoxFit.cover,
alignment: Alignment.center,
),
);
});
});
}
}
You need to wait to fetch the data
class GetImage extends StatelessWidget {
const GetImage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: FirebaseFirestore.instance
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.collection('cart')
.snapshots(),
builder: (context,
AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>> snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
return Container(
child: Image.network(
snapshot.data!.docs[index].data()['imageUrl'],
fit: BoxFit.cover,
alignment: Alignment.center,
),
);
});
}
if (snapshot.hasError) {
return Text("got error ${snapshot.error}");
}
return CircularProgressIndicator();
});
}
}

How to list favorites/bookmarks in Flutter with Firestore

I have a collection called Stuff that holds a title. Think it like a Twitter post.
{
'stuffID': string
'title': string
'details': string
}
And I have a favorites collection, that hold who favorite the which post. A user can favorite multiple stuff.
{
'userID': string
'stuffID': string
}
From second collection, I want to get all stuffID's that current user favorite. And I want to use those to get rest of the information from first collection. In summary, I want to list all stuff's that user favorite. Like a bookmark list.
I thought I must use two StreamBuilder for achieving this. But I couldn't make it work.
Here is what I manage to do:
#override
Widget build(BuildContext context) {
final FirebaseAuth auth = FirebaseAuth.instance;
final User? user = auth.currentUser;
final userID = user!.uid;
var resultStream = FirebaseFirestore.instance
.collection('favorites')
.where("userID", whereIn: [userID]).snapshots();
return StreamBuilder<QuerySnapshot>(
stream: resultStream,
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot1) {
if (snapshot1.hasError) {
return Text('Something is wrong.');
}
if (snapshot1.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
snapshot1.data!.docs.map((DocumentSnapshot document1) {
Map<String, dynamic> data1 =
document1.data()! as Map<String, dynamic>;
print(data1['stuffID']);
Query _stuffStream = FirebaseFirestore.instance
.collection('Stuffs')
.where('stuffID', isEqualTo: data1['stuffID']);
return StreamBuilder<QuerySnapshot>(
stream: _stuffStream.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot2) {
if (snapshot2.hasError) {
return Text('Something is wrong.');
}
if (snapshot2.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return ListView(
//showing the data
children:
snapshot2.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
String stuffID = data['stuffID'];
return ListTile(
title: Text(data['title']),
subtitle: Text(data['details']),
);
}).toList(),
);
});
});
return const Center(child: CircularProgressIndicator());
});
}
When I use this code, app stucks at loading screen:
I'm trying to work it for two days but all my attempts have failed. Can you please help?
I did more and more research day after day and found the answer. I created the code based on here. Note that I changed the design of my collection in Firebase. But it completely unrelated to rest of code. I just wanted to go with more efficient way to store the data.
In summary, I'm fetching the stuffID from favorites collection. And using that stuffID to get stuffs.
#override
Widget build(BuildContext context) {
final FirebaseAuth auth = FirebaseAuth.instance;
final User? user = auth.currentUser;
final userID = user!.uid;
return StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('favorites')
.doc(userID)
.collection(userID)
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return const Text("Loadsing...");
return Column(children: [
ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
//return buildListItem(context, snapshot.data.documents[index]);
return ListView(
scrollDirection: Axis.vertical,
shrinkWrap: true,
children: <Widget>[
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('Stuffs')
.where('stuffID',
isEqualTo: snapshot.data!.docs[index]
['stuffID']) //seçilen döküman
.snapshots(),
builder: (context, snap) {
if (!snap.hasData) return const Text("Loading...");
return ListTile(
leading: CircleAvatar(
backgroundImage: NetworkImage(
snap.data!.docs[index]['stuffImage']),
),
title: Text(snap.data!.docs[index]['title']),
subtitle: Column(
children: <Widget>[
Text(snap.data!.docs[index]['details']),
],
),
);
}),
],
);
}),
]);
},
);
}

Infinite loop FutureBuilder when use Provider

I have method like this in my provider file to get a list of items :
Future<void> getServerMeals() async {
final QueryBuilder<ParseObject> parseQuery =
QueryBuilder<ParseObject>(ParseObject('UsersEaten'));
final ParseResponse apiResponse = await parseQuery.query();
if (apiResponse.success && apiResponse.results != null) {
List<dynamic>? apiRes = apiResponse.results;
List<EatenItem> newMeals = apiRes!
.map((e) => EatenItem(
id: e['objectId'],
eatenCal: e['eatenCal'],
eatenTitle: e['eatenTitle'],
eatenImageUrl: e['eatenImg'],
userId: e['objectId'],
))
.toList();
_eatenMeals = newMeals;
print(apiResponse.results);
notifyListeners();
}
}
and in my main screen I have a StatelessWidget and FutureBuilder to get that list :
class TimelineWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
child: FutureBuilder(
future: Provider.of<EatenMeals>(context, listen: false).getServerMeals(),
builder: (ctx, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
return Consumer<EatenMeals>(
builder: (ctx, mealsList, child) => ListView.builder(
itemCount: mealsList.eatenMeals.length,
itemBuilder: (ctx, index) =>
DailyEaten(mealsList.eatenMeals[index]),
),
);
},
),
),
],
);
}
as you can see there is nothing in my build method and I've tried to use Consumer as future to avoid infinite loop but it doesn't work for me please tell me what's wrong here I appreciate if u explain with code snippet

I am failing to get data from cloud firestore while using flutter

At first, when i started writing my calls to get data from firestore, it worked. But when i tried writing more docs to my collection, it failed to bring data for the docs i recently added. Then, when i deleted the first one i added, i stopped receiveing data from firestore all together. I have tried several methods, but have all ended in failure.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class collect extends StatefulWidget {
#override
_collectState createState() => _collectState();
}
class _collectState extends State<collect>
{
Future _data;
void initState()
{
super.initState();
_data = getStuff();
}
Future getStuff()
async {
var firestore = FirebaseFirestore.instance;
QuerySnapshot qn = await firestore.collection("buses").get();
return qn.docs;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: _data,
builder: (_, snapshot)
{
if(snapshot.connectionState == ConnectionState.waiting)
{
return Center(
child:Text("Loading")
);
}
else if(snapshot.connectionState == ConnectionState.done)
{
return ListView.builder(itemCount: snapshot.data.length,itemBuilder:(_, index)
{
return Container(
child: ListTile(
title: Text(snapshot.data[index].data()["name"].toString()),
subtitle: Text(snapshot.data[index].data()["price"].toString()),
),
);
});
}
},
),
);
}
}
```![enter image description here](https://i.stack.imgur.com/L7FqF.jpg)
Define your database call as,
Future getStuff() async {
var docs;
await FirebaseFirestore.instance
.collection("buses")
.get()
.then((querySnapshot) {
docs = querySnapshot.docs;
});
return docs;
}
Then use the FutureBuilder in the build() function as,
return Scaffold(
body: Center(
child: FutureBuilder<dynamic>(
future: getStuff(),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (_, index) {
return Container(
child: ListTile(
title: Text(
snapshot.data[index].data()["name"].toString()),
subtitle: Text(
snapshot.data[index].data()["price"].toString()),
),
);
});
} else {
return CircularProgressIndicator();
}
},
),
),
);
I wrapped the FutureBuilder inside a Center just for clarity, you may remove that Center widget.