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.