Can't retrieve data from nested object firestore streambuilder listview - flutter

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']);
},
);
}
});

Related

Why StreamBuilder always has no data before hot reload?

I use firestore and streambuilder to read data in a list, when i run the application for the first time i get a message "Unexpected null value" and I realized that "snapshot.hasData" is always false and snapshot.ConnectionState.waiting is always true. But when i restart application with hot reload i can retrieve data.
This is my stream:
Stream<QuerySnapshot> _branchStream = FirebaseFirestore.instance.collection('Companies').doc(company_id).collection("Branch Offices").snapshots();
This is my StreamBuilder
StreamBuilder<QuerySnapshot>(
stream: _branchStream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
/* if (snapshot.hasError) {
return const Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text("Loading");
}*/
return ListView(
children: snapshot.data!.docs
.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return Padding(
padding: const EdgeInsets.all(22.0),
child: Card(
elevation: 8,
shadowColor: Colors.blueGrey,
shape: cardShape,
child: Row(
children: [
Expanded(
flex: 2,
child: Padding(
padding: const EdgeInsets.all(22.0),
child: CircleAvatar(
radius: 50,
backgroundImage:
NetworkImage(data['branch_image'],scale: 60),
),
)),
Expanded(
flex: 4,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(22.0),
child: Text(data['branch_name'], style: textBlackTitle, textAlign: TextAlign.center,),
),
Padding(
padding: const EdgeInsets.all(22.0),
child: Text("Ubicación: "+data['branch_address'], style: textGraySubTitle, textAlign: TextAlign.center,),
),
],
)),
Expanded(
flex: 2,
child: IconButton(
// focusColor: Color(color1),
// color: Color(color1),
onPressed: (){
Navigator.push(context, MaterialPageRoute(builder: (context) => Home(branch_id : data['branch_id'], company_id : company_id, branch_name : data['branch_name'], branch_image : data['branch_image'])));
}, icon: Image.asset("assets/enter.png", fit: BoxFit.contain, height: 100,)))
],
),
),
);
})
.toList()
.cast(),
);
},
)
This is data that I want to get
This is what I get at the first time
This is what I get after hot reload (That I should have from the beginning).
Because your data is null at the beginning, it takes some time to load the data.
You actually already included a check, but commented it out again. Undo it and try again.
/* if (snapshot.hasError) {
return const Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text("Loading");
}*/
It takes some time to load snapshot data. For better UX return specific widgets for each state of the snapshot.
Make sure you're using StreamBuilder inside StatefulWidget.
StreamBuilder<QuerySnapshot>(
stream: _branchStream,
builder: (BuildContext context, snapshot) {
if (snapshot.hasError) {
return //error widget
} else {
switch (snapshot.connectionState) {
case ConnectionState.none:
return //some widget
case ConnectionState.waiting:
return CircularProgressIndicator(),
case ConnectionState.active:
return ListView()
case ConnectionState.done:
return //some widget
}
}
);

Flutter - Nested Streambuilder and FutureBuilder

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.

Display sub-collection in flutter Firebase

I want to display the data of a sub-collection named "Profile". I get it that we need to query it differently, and I tried it, but it is not working out for me. First, I displayed the information from the documents of the mother collection "mentors", using StreamBuilder. Then passed it's data to a Widget I created. Then on the Widget I created, I performed another streamBuilder query for the subcollection of each document of the Mother Collection "mentors".
This is the code I used to display the documents on "mentors" collection, and is working fine.
final mentors = Expanded(
child: Container(
height: 250,
margin: const EdgeInsets.only(left: 20, right: 20),
child: StreamBuilder<QuerySnapshot>(
stream: db_mentors,
builder: (
BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot,
) {
if (snapshot.hasError) {
Fluttertoast.showToast(msg: "An Error Occured");
}
if (snapshot.connectionState == ConnectionState.waiting) {
Fluttertoast.showToast(msg: "Loading");
}
final data = snapshot.requireData;
return ListView.builder(
itemCount: data.size,
itemBuilder: ((context, index) {
return mentorsWidget(
"${data.docs[index]["uid"]}",
"${data.docs[index]['name']}",
"${data.docs[index]['specialty']}",
);
}),
);
}),
),
);
This here is the code I used to display the data from the subcollection of each document named "Profile". Which is also the widget I created.
Widget mentorsWidget(String uid, String name, String specialty) {
return Container(
margin: const EdgeInsets.all(5),
width: size.width,
decoration: const BoxDecoration(
color: Color.fromARGB(255, 3, 42, 134),
borderRadius: BorderRadius.only(
topRight: Radius.circular(20),
bottomRight: Radius.circular(20))),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
StreamBuilder(
stream: FirebaseFirestore.instance
.collection("mentors")
.doc(uid)
.collection("Profile")
.snapshots(),
builder: (
BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot,
) {
if (!snapshot.hasData) {
return SizedBox(
width: 80,
child: Image.asset("assets/Navigatu-icon.ico"),
);
} else {
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: ((context, index) {
String url = snapshot.data!.docs[index]['downloadURL'];
return SizedBox(
width: 80,
child: Image.network(url),
);
}),
);
}
}),
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: const EdgeInsets.only(top: 10, left: 5),
child: Text(
name,
style: const TextStyle(
color: Colors.white,
fontSize: 20,
fontFamily: 'Roboto',
fontWeight: FontWeight.w500),
),
),
Container(
margin: const EdgeInsets.only(top: 15, bottom: 15, left: 5),
child: Text(
specialty,
style: const TextStyle(
color: Colors.white,
fontFamily: 'Roboto',
fontWeight: FontWeight.w400,
fontSize: 12,
),
),
),
],
)
],
),
);
}
Here is the Collection Tree in my firebase:
Firebase Collection Tree
Here is the display I want to achieve. The boat picture here supposedly must be a Image.network, with the url that is in the sub-collection, named "Profile".
Mentor Field
As you can see in the code, I performed first the "final mentors", then performing streambuilder inside of it. So that I can get the datas of each document from the mother collection. Now I passed those data to the "mentorwidget" to display them in a proper way, but then I wanna use a Image.network, containing the data inside the sub-collection of each document in the mother collection. That's why I performed another streambuilder inside the mentorwidget to display the picture, or get the data of the sub-collection which is the url of the said picture.
If the data doesn't get frequently updated or if you don't need to display the constant changes of it's value to the users then use FutureBuilder instead of StreamBuilder to query the value you want only once.
class Home extends StatelessWidget {
const Home({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<QuerySnapshot>(
future: FirebaseFirestore.instance
.collection('mentors')
.doc('b23lt...[your desired document ID]')
.collection('Profile')
.get(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
return ListView.builder(
itemCount: , // lenght of snapshot data,
itemBuilder: (context, index) {
//Here you can retrieve the data of each document
},
);
}
return const Center(child: CircularProgressIndicator());
},
),
);
}
}
Update: I found the answer! thanks to Slender's answer, I managed to get the answer, here is the code.
FutureBuilder<QuerySnapshot>(
future: FirebaseFirestore.instance
.collection("mentors")
.doc(uid)
.collection("profile")
.get(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data!.docs.isEmpty) {
return SizedBox(
width: 80,
child: Image.asset("assets/Navigatu-icon.ico"),
);
} else if (snapshot.hasData) {
// print(snapshot);
return SizedBox(
width: 80,
child: Image.network(
"${snapshot.data!.docs[0]['downloadURL']}"),
);
}
}
// print(snapshot.data!.docs[0]['downloadURL']);
return const SizedBox(
width: 80,
child: Center(
child: CircularProgressIndicator(),
),
);
},
),

How to display images from Frirebase Storage in StreamBuilder

I'm trying to display images from FirebaseStorage in StreamBuilder when user add them after they have been saved in FirebaseStorage
Try #1
It works but only the last added image is shown as stream and I would like to display all the images added.
I guess this try is the better way but I can't add ListView.builder because TaskSnapshot is one data and not a list of data like ListResult that's probably why I can't diplay all images and only the last one.
Widget:
Widget loadingImage(UploadTask uploadTask) => StreamBuilder<TaskSnapshot>(
stream: uploadTask.snapshotEvents,
builder: (context, snapshot) {
if(snapshot.hasData) {
final snap = snapshot.data;
final photoName = snap!.metadata!.name;
final photoType = snap.metadata!.contentType;
final photo = snapshot.data!.ref.getDownloadURL();
return StreamBuilder(
stream: photo.asStream(),
builder: (context, AsyncSnapshot<String> snapshot) {
final image = snapshot.data;
print("Image: $image");
return Row(
children: [
Image.network(
fit: BoxFit.cover,
width: 100,
height: 100,
image!,
),
const SizedBox(width: 20,),
Text(
photoName,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(width: 20,),
Text(
photoType!,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
)
],
);
}
);
}
return Container();
},
);
Try #2
It displays images but not as stream if one is added it's not shown if a second one is added the first one will be displayed etc
DocumentViewModel:
//----------------------------------------------------------------------------
//----------------------------- Upload instructor document list --------------
//----------------------------------------------------------------------------
static Stream<ListResult>? uploadFileList(String uid) {
try {
final result = FirebaseStorage.instance
.ref("instructorDocuments/")
.child("$uid/")
.listAll();
return result.asStream();
} on FirebaseException catch (e) {
return null;
}
}
Widget:
Widget loadingImage(UploadTask uploadTask) => StreamBuilder<ListResult>(
stream: DocumentViewModel.uploadFileList(user!.uid),
builder: (context, snapshot) {
if(snapshot.hasData) {
final snap = snapshot.data;
return ListView.builder(
itemCount: snap!.items.length,
itemBuilder: (context, index) {
final photoName = snap.items[index].name;
final photoType = snap.items[index].getMetadata();
final photo = snap.items[index].getDownloadURL();
return StreamBuilder<FullMetadata>(
stream: photoType.asStream(),
builder: (context, AsyncSnapshot<FullMetadata> snapshot) {
if(snapshot.hasData) {
final type = snapshot.data!.contentType;
return StreamBuilder(
stream: photo.asStream(),
builder: (context, AsyncSnapshot<String> snapshot) {
if(snapshot.hasData) {
final image = snapshot.data;
return Row(
children: [
Image.network(
fit: BoxFit.cover,
width: 100,
height: 100,
image!,
),
const SizedBox(width: 20,),
Text(
photoName,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(width: 20,),
Text(
type!,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
],
);
}
return Container();
},
);
}
return Container();
}
);
},
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
);
}
if(!snapshot.hasData) {
return const Loader();
}
if(snapshot.hasError) {
return Utils.showErrorMessage(snapshot.hasError.toString());
}
else {
return Container();
}
}
);
Thanks in advance
try this without streambuilder for getting images from firebase
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class BlogView extends StatefulWidget {
const BlogView({Key? key}) : super(key: key);
#override
State<BlogView> createState() => _BlogViewState();
}
var im;
var allimgList = [];
class _BlogViewState extends State<BlogView> {
#override
void initState() {
getData();
super.initState();
setState(() {
});
}
void getData() async {
allimgList=[];
await FirebaseFirestore.instance
.collection('Names')
.snapshots()
.forEach((event) {
var n=event.docs.length;
print(event.docs.length);
for (int i = 0; i <n; i++) {
im = event.docs[i]['Images'];
var nn=event.docs[i]['Images'].length;
for(int j=0;j<nn;j++){
allimgList.add(im[j]);
setState(() {});
}
if(i==n)break;
}
print("=========>" + allimgList.toString());
});
setState(() {
});
}
#override
Widget build(BuildContext context) {
// print("================lllllllll =>" + allimgList.toString());
return Scaffold(
appBar: AppBar(
title: Text("Blog"),
),
body: Container(
child: Center(
child: allimgList.length == 0 ?
Container(
height: 500,
width: 300,
child: Center(child: Text("No Image Added Yet")),
):Container(
margin: EdgeInsets.only(top: 10),
child: GridView.builder(
padding: EdgeInsets.only(left: 10, right: 10),
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 150,
childAspectRatio: 4 / 4,
crossAxisSpacing: 10,
mainAxisSpacing: 20),
itemCount: allimgList.length,
itemBuilder: (BuildContext ctx, index) {
return Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15)),
child: Image.network(
allimgList[index],
fit: BoxFit.fill,
),
);
}),
),
),
),
);
}
}

Refresh Indicator does not update a list after deleting data

I have a FutureBuilder getting specific items from Firestore and returning them in a list. To update this list I added RefreshIndicator. When something is added to the list and I refresh it, it works normally and the item appears. The problem occurs when I remove an item from the list, it loads but the list remains the same.
This is my Widget:
#override
void initState() {
super.initState();
userFuture = getCollectionItems();
}
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Column(
children: [
Container(
margin: EdgeInsets.only(top: 5.5),
padding: EdgeInsets.only(left: 17, right: 17),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"Collection",
style: TextStyle(
fontFamily: 'Montserrat',
fontSize: 28,
fontWeight: FontWeight.w800,
),
),
],
),
),
createGridAndList(),
Expanded(
child: RefreshIndicator(
onRefresh: () => getCollectionItems(),
child: displayCollection(),
),
)
],
),
);
}
When I restart the app or go to another page and come back with pushNamedRemoveUntil the list updates properly, this indicates that the query is working.
getCollectionItems() and displayCollection():
getCollectionItems() async {
QuerySnapshot querySnapshot = await Firestore.instance
.collection("users")
.document(_userId)
.collection("userCollection")
.getDocuments();
List<Users> collectionID = [];
for (DocumentSnapshot item in querySnapshot.documents) {
var data = item.data;
Users user = Users();
user.id = data["id"];
collectionID.add(user);
}
final collectionIDs = collectionID.map((doc) => doc.id).toList();
var splitCollection = partition<dynamic>(collectionIDs, 10);
for (int i = 0; i < splitCollection.length; i++) {
QuerySnapshot querySnapshotCollections = await Firestore.instance
.collection('items')
.where('itemId', whereIn: splitCollection.elementAt(i))
.orderBy('timestamp', descending: true)
.getDocuments();
setState(() {
countItem = querySnapshotCollections.documents.length;
itemsList = querySnapshotCollections.documents
.map((documentSnapshot) =>
CollectionItem.fromDocument(documentSnapshot))
.toList();
});
}
}
displayCollection() {
return FutureBuilder(
future: userFuture,
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return Padding(
padding: EdgeInsets.only(top: 20),
child: SizedBox(
child: CircularProgressIndicator(
valueColor: new AlwaysStoppedAnimation<Color>
(Colors.grey),
),
width: 20.0,
height: 20.0,
),
);
}
if (itemsList == null) {
return Container(
padding: EdgeInsets.only(top: 20),
child: SizedBox(
child: CircularProgressIndicator(
valueColor: new AlwaysStoppedAnimation<Color>
(Colors.grey),
),
width: 20.0,
height: 20.0,
),
);
} else if (itemsList.isEmpty) {
return ListView(
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics()),
children: [
Center(
child: Container(
padding: EdgeInsets.only(
top: MediaQuery.of(context).size.width * 0.50,
left: 17,
right: 17),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Nothing here.",
style: TextStyle(
fontFamily: 'Montserrat',
fontSize: 13,
color: Color(0xff9e9999),
fontWeight: FontWeight.w500),
textAlign: TextAlign.center,
),
],
),
),
),
],
);
} else if (itemOrientation == "grid") {
List<GridTile> gridTilesList = [];
itemsList.forEach((eachItem) {
gridTilesList.add(GridTile(child:
CollectionItemTile(eachItem)));
});
return GridView.count(
crossAxisCount: 2,
padding: EdgeInsets.fromLTRB(10, 15, 10, 0),
childAspectRatio: 3 / 2,
mainAxisSpacing: 15,
crossAxisSpacing: 10,
shrinkWrap: true,
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics()),
children: gridTilesList,
);
} else {
return Container(
padding: EdgeInsets.only(bottom: 15),
child: ListView(
padding: EdgeInsets.all(0.0),
shrinkWrap: true,
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics()),
children: itemsList),
);
}
});
}
I've tried several things, switched to Stream (it didn't work), added another setState to the Widget itself, rebuilt the classes but the problem persists.
Hmmm, your displayCollection widget is displaying data based on userFuture, but halfway through you using itemList instead, and your onRefresh function is updating the itemList but not userFuture.
I won't do exactly like you do, but i refactored a bit.
You can try something like this, i didn't test it but let me know if it works 😊
// I changed `userFuture` to `futureItems`
Future<List<CollectionItem>> futureItems;
#override
void initState() {
super.initState();
futureItems = getCollectionItems();
}
Future<List<CollectionItem>> getCollectionItems() async {
// ... Do your query here
return querySnapshotCollections.documents.map((documentSnapshot) {
return CollectionItem.fromDocument(documentSnapshot);
}).toList();
}
Future<void> refreshCollectionItems() async {
setState(() {
// This will update the futureItems
futureItems = getCollectionItems();
});
}
Widget displayCollection() {
return FutureBuilder<List<CollectionItem>>(
future: futureItems, // The data returned will be inside `snapshot` below
builder: (context, snapshot) {
if (snapshot?.hasData ?? false) {
List<CollectionItem> items = snapshot.data; // This is the return value from `futureItems`
return RefreshIndicator(
onRefresh: refreshCollectionItems,
child: ListView.builder(
itemCount: items.length, // This is how to get the length, so no need to use `countItem`
itemBuilder: (context, index){
CollectionItem item = items[index];
return // ...Display your widget with item data
},
),
);
}
return // Display widget to handle loading/error/no data
},
);
}
Plus it is important to define the return type of a function so that you will know what you will get after executing a function.
One of the simplest ways to solve this is re-setting the state onRefresh.
Expanded(
child: RefreshIndicator(
onRefresh: () {
getCollectionItems();
setState(() {
userFuture = getCollectionItems();
});
},
child: displayCollection(),
),
),
Your Firestore query might be reading from cache. Try disable persistence. Official Tutorial
FirebaseFirestore.instance.settings = Settings(persistenceEnabled: false);
Or if want to clear any persisted data, you can call the clearPersistence() method.
await FirebaseFirestore.instance.clearPersistence();