Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent ExpansionTile from resetting when setState is called

Tags:

flutter

dart

I am following the example from the official docs: https://flutter.io/catalog/samples/expansion-tile-sample/

I have added a StreamSubscription that returns every time a Firebase database has a new entry. Everytime this returns, I update data in the example and call setState(){data = newData}.

This theoretically should rebuild the whole ListView with new values and it works just fine however, the big problem is that when the rebuild happens, all of the ExpansionTiles collapse to the default state making the interface completely unusable if updates are frequent.

Does anyone know a way to use the example from the official docs but not have the ExpansionTiles collpased every time?

like image 645
jgv115 Avatar asked Nov 29 '25 15:11

jgv115


1 Answers

The reason state for the ExpansionTile state loss is the fact that it no longer has the same key after data is re-generated. Here is the experiment I ran on top of the documentation code you linked in your question. I am focusing only on the key part, which is where you assign keys to your ExpansionTile:

Widget _buildTiles(Entry root) {
  if (root.children.isEmpty) return new ListTile(title: new Text(root.title));
  return new ExpansionTile(
    key: new PageStorageKey<Entry>(root),
    title: new Text(root.title),
    children: root.children.map(_buildTiles).toList(),
  );
}

Because you are most likely working with immutable data, when you get new data from Firebase, you generate new objects from it. Even though the root has the same data and is the same to you, it is not the same object for Flutter. You now have 2 ways of dealing with this. One is to override == which in my opinion is a bad idea and will explain why with a comment. The second would be to generate your keys from data attributes you know will be the same (like unique database keys). I chose the title attribute for my example.

Widget _buildTiles(Entry root) {
  if (root.children.isEmpty) return new ListTile(title: new Text(root.title));
  return new ExpansionTile(
    key: new PageStorageKey<String>(root.title), // root.title == newerRoot.title is true
    title: new Text(root.title),
    children: root.children.map(_buildTiles).toList(),
  );
}

You can try it out yourself using the updated code in this gist. I also had to make some modifications to the original code from the documentation so I could refresh the data and simulate your situation.

like image 159
Andrei Tudor Diaconu Avatar answered Dec 02 '25 05:12

Andrei Tudor Diaconu



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!