I am trying to run a query on my Firebase Firestore data and display the results as a list. Typically, when I do this, I use a streambuilder, which updates the results whenever any data is changed. I was wondering how to display the same data only once, without subscribing to a stream. This is my code for the streambuilder, which updates on change.
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('announcements')
.orderBy('date', descending: true)
.snapshots(),
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> snapshots) {
if (snapshots.connectionState == ConnectionState.active &&
snapshots.hasData) {
return ListView(
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
children: snapshots.data!.size == 0
? [
const Padding(
padding: EdgeInsets.only(top: 10),
child: Text(
"You have no announcements",
style: TextStyle(
height: 1.0,
fontFamily: "urwmedium",
color: Color(0xffD5D6D7),
fontSize: 20,
),
),
),
]
: snapshots.data!.docs.map(
(DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return Text(
data['announcementTitle'],
);
},
).toList(),
);
}
return Center(
child: CircularProgressIndicator(
color: CustomColors.textColor,
),
);
},
),
The above code works, but again, it loads information realtime. What would the code be if I wanted it to only load once at opening?
as the StreamBuilder is helpful in order to get stream of your collection snapshots
to get that data only once, use FutureBuilder instead:
FutureBuilder<QuerySnapshot>(
future: FirebaseFirestore.instance
.collection('announcements')
.orderBy('date', descending: true)
.get(),
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> snapshots) {
if (snapshots.connectionState == ConnectionState.done &&
snapshots.hasData) {
return ListView(
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
children: snapshots.data!.size == 0
? [
const Padding(
padding: EdgeInsets.only(top: 10),
child: Text(
"You have no announcements",
style: TextStyle(
height: 1.0,
fontFamily: "urwmedium",
color: Color(0xffD5D6D7),
fontSize: 20,
),
),
),
]
: snapshots.data!.docs.map(
(DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return Text(
data['announcementTitle'],
);
},
).toList(),
);
}
return Center(
child: CircularProgressIndicator(
color: CustomColors.textColor,
),
);
},
),
Related
am trying to retrieve current user data but it showing all the users data from firestore in app
here is my code
Widget build(BuildContext context){
CollectionReference users = FirebaseFirestore.instance.collection('users');
return FutureBuilder<DocumentSnapshot>(
future: users.doc(documentId).get(),
builder: ((context, snapshot){
if(snapshot.connectionState == ConnectionState.done){
Map<String, dynamic> data =
snapshot.data!.data() as Map<String, dynamic>;
return Container(
child: Column(
children: [
Text('Email : '"${data['Email name'].toString()}",
style: const TextStyle(fontWeight: FontWeight.bold,wordSpacing: 10),),
const SizedBox(
width: 10,
),
Text('Contact : '"${data['age'].toString()}",
style: const TextStyle(fontWeight: FontWeight.bold,wordSpacing: 10),),
const SizedBox(
width: 10,
),
Text('Name : '"${data['firstname'].toString()}",
style: const TextStyle(fontWeight: FontWeight.bold,wordSpacing: 10),),
const SizedBox(
width: 10,
),
Text('Last Name : '"${data['lastname'].toString()}",
style: const TextStyle(fontWeight: FontWeight.bold,wordSpacing: 10),),
const SizedBox(
width: 10,
),
],
),
);
}
return const Text('Loading');
}),
here is documentId code
List<String> docIDs = [];
Future getDocId() async{
await FirebaseFirestore.instance.collection('users').get().then(
(snapShot) => snapShot.docs.forEach(
(documant){
print(documant.reference);
docIDs.add(documant.reference.id);
}));
}
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// ignore: prefer_const_constructors
Text('SignUp as ${user!.email!}',style: TextStyle(
fontSize: 20,
),),
Expanded(child: FutureBuilder(
future: getDocId(),
builder: (context, snapshot){
return ListView.builder(
itemCount: docIDs.length,
itemBuilder: (context, index){
return ListTile(
title: GetuserData(documentId: docIDs[index]),
);
},
);
},
))
when ever user post the form it submitted successfully and generate a post like summited form history but it showing to others users to but i wont it for only current user how to do that please any one can help me
You can use firestore query to return the current user using where()
on getDocId():
await FirebaseFirestore.instance.collection('users').get()
change to
await FirebaseFirestore.instance.collection('users')
.where('Email name', isEqualTo: 'myEmail#mail.test').get()
you can replace the email with your or consider using parameter to pass user email dynamically.
I have categories collection with category name and id fields. In foods collection, the category is a reference field. I need to display like this:click to see expected output
where title is coming from categories collection and foods are coming from foods collection.
I tried using nested streambuilder: streambuilder 1: fetch categories in a listview streambuilder 2: fetch foods in a list. Inside streambuilder 2, i have used a futurebuilder to decode the category data. If category name in food and category name from streambuilder 1 is same, the food will be displayed under that category.+
class RestaurantDetails extends StatefulWidget {
final String id;
RestaurantDetails({required this.id, super.key});
#override
State<RestaurantDetails> createState() => _RestaurantDetailsState();
}
class _RestaurantDetailsState extends State<RestaurantDetails> {
List<FoodCategory> categories = [];
List<Food> foods = [];
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: StreamBuilder(
stream: getCategories(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasData) {
categories = snapshot.data!.docs
.map((item) => FoodCategory.fromMap(item))
.toList();
return ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: categories.length,
itemBuilder: ((context, cateindex) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 10),
child: Container(
height: 30,
width: MediaQuery.of(context).size.width * 1,
color: Colors.white,
child: Text(
categories[cateindex].name.toString(),
style: Theme.of(context)
.textTheme
.headline4!
.copyWith(fontSize: 20.0),
),
),
),
StreamBuilder(
stream: FirebaseFirestore.instance
.collection('foods')
.doc(widget.id)
.collection('all')
.snapshots(),
builder: (context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasData) {
foods = snapshot.data!.docs
.map((item) => Food.fromMap(item))
.toList();
return ListView.builder(
itemCount: foods.length,
physics:
const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: ((context, foodindex) {
var catepath = foods[foodindex].cid!.path;
String cateDocumentName = catepath
.substring(11, catepath.length);
return Column(
children: [
FutureBuilder(
future: FirebaseFirestore.instance
.collection('categories')
.doc(cateDocumentName)
.get(),
builder: ((context,
AsyncSnapshot<
DocumentSnapshot>
snapshot) {
if (snapshot.hasData) {
Map<String, dynamic> data =
snapshot.data!.data()
as Map<String,
dynamic>;
if (data['name'] ==
categories[cateindex]
.name) {
return Padding(
padding: const EdgeInsets
.symmetric(
vertical: 10,
horizontal: 10),
child: Container(
decoration: BoxDecoration(
borderRadius:
BorderRadius
.circular(
20),
color:
Colors.white),
height: 100,
width: MediaQuery.of(
context)
.size
.width *
1,
child: Row(
children: [
Image.network(
foods[foodindex]
.cover
.toString(),
height: 100,
width: 100,
errorBuilder:
((context,
error,
stackTrace) {
return Image
.asset(
'assets/images/food1.jpg',
height: 100,
width: 100,
);
}),
),
UIHelper
.horizontalSpaceMedium(),
Column(
crossAxisAlignment:
CrossAxisAlignment
.start,
children: [
UIHelper
.verticalSpaceSmall(),
Text(
foods[foodindex]
.name
.toString(),
style: Theme.of(
context)
.textTheme
.bodyText1,
),
UIHelper
.verticalSpaceSmall(),
Text(
'₹${foods[foodindex].price}',
style: Theme.of(
context)
.textTheme
.bodyText1!
.copyWith(
fontSize:
14.0),
),
UIHelper
.verticalSpaceMedium(),
],
)
],
)),
);
} else {
return const SizedBox();
}
} else {
return const CircularProgressIndicator
.adaptive();
}
}))
],
);
}));
} else {
return const CircularProgressIndicator
.adaptive();
}
},
)
],
);
}));
} else {
return const CircularProgressIndicator.adaptive();
}
}),
),
);
}
getCategories() {
return FirebaseFirestore.instance
.collection('categories')
.where('uid', isEqualTo: widget.id)
.snapshots();
}
}
categories data
click to see categories
food data
click to see food data
I get the output.see my output here but when data is large (i.e large number of foods inside a category) the app hangs. Is there anyother way we can achieve this? the data should load seamlessly regardless of data size.
I'm new using firestore, so im still trying to understand it.
i had Closets on the inside i had Clothes. i want to retrieve Clothes data and show it with listview.
the problem is i failed to retrieve the data and show it into the app
this is my code for the streambuilder
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collection("clothes").snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text("Failed to load data!");
}
if (snapshot.connectionState ==
ConnectionState.waiting) {
return ActivityServices.loadings();
}
return new ListView(
children: snapshot.data.docs
.map((DocumentSnapshot doc) {
Clothes clothes;
clothes = new Clothes(
doc.data()['clothesId'],
doc.data()['clothesName'],
doc.data()['clothesDesc'],
doc.data()['clothesImage'],
doc.data()['clothesCloset'],
doc.data()['clothesAge'],
doc.data()['clothesTag'],
doc.data()['clothesStatus'],
doc.data()['clothesLaundry'],
doc.data()['createdAt'],
doc.data()['updatedAt'],
);
print(doc.data()['clothesName']);
return CardClothesLemari(clothes: clothes);
}).toList(),
);
},
),
and this is my CardClothesLemari
final Clothes clothes;
CardClothesLemari({this.clothes, this.doc});
#override
_CardClothesLemariState createState() => _CardClothesLemariState();
}
class _CardClothesLemariState extends State<CardClothesLemari> {
#override
Widget build(BuildContext context) {
Clothes cloth = widget.clothes;
final Size size = MediaQuery.of(context).size;
if (clothes == null) {
return Container();
} else {
return Padding(
padding:
EdgeInsets.only(top: 5.0, bottom: 5.0, left: 5.0, right: 5.0),
child: InkWell(
onTap: () {
Navigator.pushNamed(context, DetailClothes.routeName,
arguments: cloth);
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(14.0),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2),
spreadRadius: 3.0,
blurRadius: 5.0)
],
color: Color(0xffA77665),
),
child: Column(children: [
Padding(
padding: EdgeInsets.only(top: size.height * 0.04),
),
Hero(
tag: 'assets/images/dummy.jpg',
child: CircleAvatar(
radius: 55,
backgroundImage: AssetImage("assets/images/dummy.jpg"),
),
),
SizedBox(height: 7.0),
Text(
//show clothes name
cloth.clothes,
style: TextStyle(
fontSize: 14,
fontFamily: GoogleFonts.openSans().fontFamily,
fontWeight: FontWeight.w700,
color: Color(0xffF0E8E1)),
textAlign: TextAlign.center,
),
Padding(
padding: EdgeInsets.only(top: 8),
child:
Container(color: Color(0xFFEBEBEB), height: 2.9657),
),
]))));
}
}
}
this is the screenshot of my firestore
Add listview inside the ConnectionState.done like below code.
if (snapshot.connectionState == ConnectionState.done) {
return new ListView(
children: snapshot.data.docs
.map((DocumentSnapshot doc) {
Clothes clothes;
clothes = new Clothes(
doc.data()['clothesId'],
doc.data()['clothesName'],
doc.data()['clothesDesc'],..........<Rest of the code>......
}
As per your database structure you're entering wrong query. Kindly take a look below code
StreamBuilder<QuerySnapshot<Map<String, dynamic>>>(
stream: FirebaseFirestore.instance
.collection('closet')
.doc('your_document_id')
.collection('clothes')
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Text('Loading...');
} else {
return ListView.builder(
itemCount: snapshot.data.docs.length,
shrinkWrap: true,
itemBuilder: (context, int index) {
QueryDocumentSnapshot<Map<String, dynamic>> data = snapshot.data.docs[index];
return Text(data.data()['clothesName']);
},
);
}
});
I am using StreamBuilder to get only those docs from the articles collection which satisfies the condition. I used where clause but it is getting all docs and sorting them.
(Note: I want only those docs which satisfy condition in where not all.)
updated code detail: Used another streambuilder to get the ids from the following field in a doc of users collection.
Here how am I doing it:
StreamBuilder<DocumentSnapshot>(
stream: FirebaseFirestore.instance
.collection('users')
.doc(profileScreenController.currentUser.uid)
.snapshots(),
builder: (context,
AsyncSnapshot<DocumentSnapshot> futureSnapShot) {
if (!futureSnapShot.hasData &&
futureSnapShot.data == null) {
return SizedBox();
}
DocumentSnapshot documentSnapshot =
futureSnapShot.data;
return Container(
height: screenHeight * 0.32,
child: StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('articles')
.where('userId',
isEqualTo: documentSnapshot['following'])
.snapshots(),
builder: (context,
AsyncSnapshot<QuerySnapshot> snapshot) {
print(documentSnapshot['following']);
if (!snapshot.hasData &&
snapshot.data == null) {
return Center(
child: Lottie.asset('assets/loading.json'),
);
}
if (snapshot.data.docs.length == 0) {
return Center(
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Lottie.asset('assets/no articles.json',
height: 140),
SizedBox(
height: 10,
),
Text(
'No body yet',
style: TextStyle(
fontSize: 20,
color: Theme.of(context)
.textTheme
.headline1
.color,
fontWeight: FontWeight.bold,
),
),
],
),
);
}
return ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: snapshot.data.docs.length,
padding: EdgeInsets.only(left: 15),
itemBuilder: (context, index) {
selectedIndex = index;
DocumentSnapshot todaysArticles =
snapshot.data.docs[index];
return Container(
width: screenWidth * 0.8,
margin: EdgeInsets.only(right: 6),
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(30),
),
child: Text(todaysArticles.data()
['title'],
),
);
});
},
),
);
}),
Result: getting doc of both users but not only where username is Guman as my where said.
My database with 2 docs in articles collection:
I believe the issue here is that the value of profileScreenController.followerName is null when creating the stream.
When sending a query like where('db_field', isEqualTo: null) the database just returns all the values from the previous query (which in your case is the whole collection).
Please check & debug if the value of profileScreenController.followerName is not null when you first open the stream.
Maybe you forgot to use a setState somewhere?
My method getting the data from db and displaying on the console. I tried several hints given in the other posts as well with no luck.
_getUsers() async {
print("getting");
var data = await http.post("http://10.0.2.2/Flutter/getdata.php", body: {
"date": formattedDate,
});
var jsonData = json.decode(data.body);
print(jsonData);
}
However the future builder not able to display:
new FutureBuilder(
future: _getUsers(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasError) {
return Center(
child: new Text('Error ${snapshot.error}'),
);
} else {
return Center(
child: Padding(
padding: const EdgeInsets.fromLTRB(56.0, 8.0, 56.0, 8.0),
//Here I guarded against the null as well:
child: ListView.builder(
itemCount: snapshot.data.length == null // showing error here
? 0
: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: new Text(
'${snapshot.data[index]["branch"]}',
style: TextStyle(
color: Colors.white,
fontSize: 25.0,
),
),
trailing: new Text(
'${snapshot.data[index]["count(`branch`)".toString()]}',
style: TextStyle(
color: Colors.white,
fontSize: 25.0,
),
),
);
}),
),
);
}
}),
How can I solve the issue?
you should return jsonData in _getUser().
getUsers() async {
print("getting");
var data = await http.post("http://10.0.2.2/Flutter/getdata.php", body: {
"date": formattedDate,
});
var jsonData = json.decode(data.body);
return jsonData;
}
, and change this
itemCount: snapshot.data.length == null // showing error here
? 0
: snapshot.data.length,
to this
itemCount: snapshot.data?.length ?? 0,
snapshot.data? checks whether the data is null or not. ?? executes its successor when the predecessor is null.
Your function doesn't return any Future, therefore the FutureBuilder is unable to get a Future to run on.
_getUsers() {
print("getting");
return http.post("http://10.0.2.2/Flutter/getdata.php", body: {
"date": formattedDate,
});
}
It needs to return a Future, you shouldn't be using await because the FutureBuilder depends on an actual Future, not data. You obtain the data inside the builder and then decode it.
new FutureBuilder(
future: _getUsers(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasError) {
return Center(
child: new Text('Error ${snapshot.error}'),
);
} else if (snapshot.hasData) { // checking for data
return Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 56, vertical: 8),
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: new Text(
'${snapshot.data[index]["branch"]}',
style: TextStyle(
color: Colors.white,
fontSize: 25.0,
),
),
trailing: new Text(
'${snapshot.data[index]["count(`branch`)".toString()]}',
style: TextStyle(
color: Colors.white,
fontSize: 25.0,
),
),
);
}),
),
);
}
}),