Search code examples
flutterlistviewstatefulwidget

Dismissible widget is still part of the tree


I try to understand a behavior between 2 stateful widgets. I have a simple listeView and each Item is an ItemTest. I created in the ItemTestState a property that is initialized with the widget.item in InitState(). With this code I got a "Dismissible widget is still part of the tree" when a try to remove an item, and I don't know why.

class DismissibleExample extends StatefulWidget {
  const DismissibleExample({super.key});

  @override
  State<DismissibleExample> createState() => _DismissibleExampleState();
}

class _DismissibleExampleState extends State<DismissibleExample> {
  List<int> items = List<int>.generate(100, (int index) => index);

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: items.length,
      padding: const EdgeInsets.symmetric(vertical: 16),
      itemBuilder: (BuildContext context, int index) {
        return ItemTest(
            item: items[index],
            onDismissed: () {
              setState(() {
                items.removeAt(index);
              });
            },
        );
      },
    );
  }
}

class ItemTest extends StatefulWidget {
  const ItemTest({
    super.key,
    required this.item,
    required this.onDismissed,
  });

  final int item;
  final Function() onDismissed;

  @override
  State<ItemTest> createState() => _ItemTestState();
}

class _ItemTestState extends State<ItemTest> {
  int item = 0;

  @override
  void initState() {
    item = widget.item;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Dismissible(
      background: Container(
        color: Colors.green,
      ),
      key: ValueKey<int>(item),
      onDismissed: (direction) {
        widget.onDismissed();
      },
      child: ListTile(
        title: Text(
          'Item $item',
        ),
      ),
    );
  }
}

enter image description here

Thanks for your help


Solution

  • The issue you’re facing is due to the way you’re handling the key in your Dismissible widget.

    you’re initializing item with widget.item in the initState() method. This value is not updated when the widget rebuilds.

    return Dismissible(
          background: Container(
            color: Colors.green,
          ),
          key: ValueKey<int>(widget.item),
          onDismissed: (direction) {
            widget.onDismissed();
          },
          child: ListTile(
            title: Text(
              'Item $item',
            ),
          ),
        );