Search code examples
flutterflutter-listviewflutter-state

Flutter - ListView's items' row tile not updating on state build


The list doesn't update after an item removed. It only removes the last row. I simplified the code as much as possible since I can't see anything wrong elsewhere. Either this is weird or I was missing something.

class ListState extends State<ListWidget>{

  ...

  List<Item> list = [];

  @override
  Widget build(BuildContext context) {
    print([for(Item item in list) item.name]); //The list is updated so nothing wrong here
    List<Tile> children = [];
    for(Item item in list) children.add(Tile(item) .. parent = this);
    return ListView(children : children, controller : controller);
  }

  void remove(Item item) => setState((){
    list.remove(item);
  });

  ...
}

class Tile extends StatefulWidget{

  Item item;
  ListState parent;

  Tile(this.item);

  @override
  State<StatefulWidget> createState() => TileState() .. item = item;
}

class TileState extends State<Tile>{

  Item item;

  @override
  Widget build(BuildContext context) => ListTile(
      key: ValueKey(item.name),
      title: Text(item.name),
      onTap: () => widget.parent.remove(item),
    );
}

Solution

  • The issue occurs from callback, you can direclty using VoidCallBack

    Changes on Tile

    class Tile extends StatefulWidget {
      final _HomeItem item;
      final Function delete;
    
      Tile(this.item, this.delete);
    
      @override
      State<StatefulWidget> createState() => _TileState();
    }
    
    class _TileState extends State<Tile> {
      @override
      Widget build(BuildContext context) {
        return ListTile(
          title: Text("${widget.item.title}"),
          subtitle: Text("${widget.item.index}"),
          onTap: () => widget.delete(),
        );
      }
    }
    

    And use case

    return Tile(items[index], () {
      items.remove(items[index]);
      setState(() {});
    });
    

    Full Snippet

    
    class MyHomePage extends StatefulWidget {
      final String title = "F";
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      final List<_HomeItem> items = List.generate(
        5,
        (i) => _HomeItem(
          i,
          'Tile n°$i',
        ),
      );
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: Center(
            child: _buildList(context),
          ),
        );
      }
    
      Widget _buildList(BuildContext context) {
        return ListView.builder(
          itemBuilder: (context, index) {
            return Tile(items[index], () {
              items.remove(items[index]);
              setState(() {});
            });
          },
          itemCount: items.length,
        );
      }
    }
    
    class Tile extends StatefulWidget {
      final _HomeItem item;
      final Function delete;
    
      Tile(this.item, this.delete);
    
      @override
      State<StatefulWidget> createState() => _TileState();
    }
    
    class _TileState extends State<Tile> {
      @override
      Widget build(BuildContext context) {
        return ListTile(
          title: Text("${widget.item.title}"),
          subtitle: Text("${widget.item.index}"),
          onTap: () => widget.delete(),
        );
      }
    }
    
    class _HomeItem {
      const _HomeItem(
        this.index,
        this.title,
      );
    
      final int index;
      final String title;
    }