4

I am trying to build a ListView where the state of each item is provided using the provider/consumer pattern. Items can be added using a button and removed by tapping on them.

I have two state classes:

  • ListState holds a list of strings (those strings will be displayed in the list)
  • ListItem holds the state for each item.

This approach works fine and the ListView contains the expected items. However if items get removed and added the list of the ListState contains the correct items. In the ListView a weired state is displayed:

enter image description here

This is my widget which holds the Scaffold and ListView:

class ListExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      builder: (BuildContext context) => ListState(), // <-- build state for the list
      child: Scaffold(
        appBar: AppBar(
          title: Text('Provider in list example'),
          actions: <Widget>[
            Consumer(
              builder: (BuildContext context, ListState state, Widget child) {
                return IconButton(
                  icon: Icon(Icons.add),
                  onPressed: () {
                    state.addItem();
                  },
                );
              },
            )
          ],
        ),
        body: Consumer(
          builder: (BuildContext context, ListState state, Widget child) {
            return ListView.builder(
              itemBuilder: (BuildContext context, int index) => ChangeNotifierProvider(
                builder: (BuildContext context) => ListItem(state.list[index]),
                child: ListItemWidget(),
              ),
              itemCount: state.list.length,
            );
          },
        ),
      ),
    );
  }
}

The ListItemWidget consumes the state of the item itself and the state of the list. In the onTap event the current item is removed from the list:

class ListItemWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer(builder: (BuildContext context, ListState listState, Widget child) {
      return Consumer(builder: (BuildContext context, ListItem itemStaet, Widget child) {
        return ListTile(
          title: Text(itemStaet.value),
          onTap: () {
            listState.removeItem(itemStaet.value);
          },
        );
      });
    });
  }
}

The two state classes are here:

class ListState with ChangeNotifier {
  static int counter = 0;

  List<String> list;

  ListState() {
    list = new List<String>();

    addItem();
    addItem();
  }

  void addItem() {
    list.add("List item $counter");
    counter++;

    notifyListeners();
  }

  void removeItem(String item) {
    list.remove(item);
    notifyListeners();
  }
}

class ListItem with ChangeNotifier {
  final String value;

  ListItem(this.value);
}

Node: The same effect happens on a GridView

Bennik2000
  • 1,112
  • 11
  • 25

0 Answers0