I am trying to create a ListView
widget that contains ExpansionTiles
and I want to have only a single tile expanded at a time. I am using quite a bit of data, so I am using ListView.builder
I am getting an error:
Error: Assertion failed:
_state != null
Documentation also specifies that you cannot call controller methods inside a build method.
My code looks like this:
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return ExpansionTile(
controller: expansionControllers[index],
leading: Text(data[index].code),
title: Text(data[index].name),
onExpansionChanged: (value) async {
if (value) {
setState(() {
prevIndexExpanded = index;
});
//Do stuff
}
},
children: [...]
);
},
);
where I have a prevIndexExpanded
which tracks the currently expanded tile and I have generated a list of ExpansionTileController()
for each tile.
final expansionControllers = List.generate(
data.length,
(index) => ExpansionTileController(),
);
So to be clear: when selecting a tile it should expand and if there is another already expanded tile, it should collapse, so there is only one expanded at a time.
As it turns out, I was making the mistake of generating the list inside of the build
method of the Widget, therefore it was generating a new list on every rebuild and the controllers wouldn't match. After taking it outside, everything is working as expected.
Quick note: If you are using a ListView.builder
with a lot of ExpansionTiles
, you should specify the initiallyExpanded
property, since while scrolling, the ListView rebuilds and it will collapse expanded tiles otherwise.