Search code examples
listviewflutterrefresh

How to update ListView when deleting a widget from it?


Im a beginner developer in Flutter and I'm trying to create a list of custom Card widget PositionWidget inside Favorite widget. The informations for very card is get through and api. I have added a delete button on every PositionWidget of the list, to delete this widget from the list, but i don't know how to refresh the list when the delete button is pressed and the element deleted from List<String> favorites.Does anyone know how to do this or can link some resources to learn this? Thank you!

class Favourites extends StatefulWidget {
  @override
  _FavouritesState createState() => _FavouritesState();
}

class _FavouritesState extends State<Favourites> {

  List<String> favourites = List<String>();
  final dio = new Dio();

  Future<List<String>> getData() async {
    favourites=MySharedPreferences().favoritePositions;
    List<String> dates = List<String>();
    for (var i=0; i < favourites.length; i++) {
      Response response = await dio.get('http://...api');
      dates.add(response.data['data']);
    }
    return dates;
  }
  Widget getFavourites() {
    return FutureBuilder(
        future: getData(),
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            if (snapshot.data.length > 0) {
              return ListView.builder(
                  itemCount: snapshot.data.length,
                  itemBuilder: (context, index) {
                    return Container(
                      padding: EdgeInsets.fromLTRB(10, 10, 10, 5),
                      height: 140,
                      width: double.maxFinite,
                        child: PositionWidget(key: new Key(index.toString()), streetName: favourites[index], date: snapshot.data[index])
                    );
                  }
              );
            } 
          }
        }
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: getFavourites(),
    );
  }
}

class PositionWidget extends StatefulWidget {
   final String streetName;
   String date;
   PositionWidget({Key key, this.streetName, this.date}) : super(key: key); 

  @override
  _PositionWidgetState createState() => _PositionWidgetState();

  ....
}



Solution

  • So you have a StatefulWidget - lets take advantage of that. I cannot check some of my code since I don't have a working sample, and I i'm away from me desk. But the gist would be:

    1. Create a local variable that stores the result of the api call
    2. Use SetState to update it so that the UI knows its been updated
    3. Change the ListView so that it uses this new variable (_dates)
    4. Set the body check if variable is set, while unset show a loading indicator
    5. Create a voidcallback on the PositionWidget that can be used on the parent widget
    6. Have the parent widget call a void function that deletes removes the item at the index specified while settingstate so that the ui knows.

    I really hope this code is right, if not, let me know and I can try to help :) - its been a while since I've used setState to interact with an API so I might have done something wrong there - I usually use BLOC to manage states so please let me know if you need more help. Here is the sample code:

    class Favourites extends StatefulWidget {
      @override
      _FavouritesState createState() => _FavouritesState();
    }
    
    class _FavouritesState extends State<Favourites> {
      List<String> _dates;
    
      List<String> favourites = List<String>();
      final dio = new Dio();
    
      Future<List<String>> getData() async {
        favourites=MySharedPreferences().favoritePositions;
        List<String> dates = List<String>();
        for (var i=0; i < favourites.length; i++) {
          Response response = await dio.get('http://...api');
          dates.add(response.data['data']);
        }
        return dates;
      }
    
      @override
      void initState() { 
        super.initState();
    
        getData().then((dates) {
          if(mounted) setState(() => _dates = dates);
        })//You should catch error here too....
      }
    
      void _deleteItem(int index) {
        if(mounted) setState(() {
          _dates.removeAt(index);
          //I assume you want to remove favorites as well otherwise the two indeces will go out of sync? Maybe?
          //favourites.removeAt(index)
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: _dates == null ?
          Center(child: CircularProgressIndicator()) :
          ListView.builder(
            itemCount: _dates.length,
            itemBuilder: (context, index) {
              return Container(
                padding: EdgeInsets.fromLTRB(10, 10, 10, 5),
                height: 140,
                width: double.maxFinite,
                  child: PositionWidget(key: new Key(index.toString()), streetName: favourites[index], date: _dates.data[index], onDelete: () => _deleteItem(index))
              );
            },
          );
        );
      }
    }
    
    class PositionWidget extends StatefulWidget {
       final String streetName;
       String date;
       final VoidCallBack onDelete;
       PositionWidget({Key key, this.streetName, this.date, @required this.onDelete}) : super(key: key); 
    
      @override
      _PositionWidgetState createState() => _PositionWidgetState();
    
      ///Wherever you have a button or gesture detector or whatever you can click/tap you can call widget.onDelete():
      ///For eg, onTap: () => widget.onDelete()
      ....
    }