Search code examples
flutterflutter-futurebuilder

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.


Solution

  • 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.