Why "initState()" method of all list items triggered every time when new item inserted at 0 index?

Viewed 1262

I'm want to add new widget (StatefulWidget) to ListView to the top of the list items.insert(0, listItem), but when I do this, all elements of the list are initializing again (initState() hook is calling every time)

If i'm adding the items to bottom of the list items.add(listItem) - everything works as intended (initState() is called only on the newly added element.)

Code for demo purposes

ListItem widget code

class ListItem extends StatefulWidget {
  final String label;

  const ListItem({Key key, @required this.label}) : super(key: key);

  _ListItemState createState() => _ListItemState();
}

class _ListItemState extends State<ListItem> {
  @override
  void initState() {
    super.initState();
    print('initState() ${this.widget.label}');
  }

  @override
  Widget build(BuildContext context) {
    return Text(this.widget.label);
  }
}

List widget code

Adding new elements occurs inside addItem() function:

class ItemsList extends StatefulWidget {
  State<StatefulWidget> createState() => ItemsListState();
}

class ItemsListState extends State<ItemsList> {
  var items = List<ListItem>();

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
        child: ListView(children: items),
        onTap: addItem);
  }

  void addItem() {
    final label = items.length.toString();
    print('Add $label');

    final listItem = ListItem(key: ValueKey(label), label: label);
    items.insert(0, listItem);

    this.setState(() {
      items = []..addAll(items);
    });
  }
}

Results

Console output when addItem() function was called 3 times (just for example):

Add 0
initState() 0
Add 1
initState() 1
initState() 0
Add 2
initState() 2
initState() 1
initState() 0

Expected output, when new items appends to bottom of the list (items.add(listItem) instead of items.insert(0, listItem))

Add 0
initState() 0
Add 1
initState() 1
Add 2
initState() 2

Is there any workarounds for this situation? I want to previous added list items staying with old state.

I tried to change axis direction of list viewport to up (changing reverse property of ListView to true) but got the same result.

1 Answers

That's because you are changing the order keys of your items.

      ListView(
            children: items,
            reverse: true,
          )

and

     final listItem = ListItem(key: ValueKey(label), label: label);
        items.add(listItem);

        this.setState(() {
          items = List.from(items);
        });

should work

Related