'Flutter Expanding List Optimisation, Performance and Best Practices
What is the best practice for displaying a list of Widgets in flutter, where the list can be extended or reduced in length via State.
Let's say we have a Provider State that determines the length and we render widgets accordingly.
List _state = Provider.of<Somesuch>(context).routes;
for(int i = 0; i < _state.length; i ++)
Container(child: Text('Item $I'),
The problem on testing this is that if the state length increases, all child widgets seem to be rebuilt. Due to the number of animations contained in each actual child, this does not seem to be an optimal approach.
We have experimenting with nesting the children:
class NestContainer extends StatelessWidget {
NestContainer({
this.child = const SizedBox(),
this.nextChild = const SizedBox(),
});
final Widget child, nextChild;
}
Widget build(BuildContext context) {
return Stack(children:[
child,
nextChild,
]);
}
then as the child of some Widget:
Builder(builder: (context) {
List _state = Provider.of<Somesuch>(context).routes;
buildContainer(int index){
return NestContainer(
child: Container(child: Text('Item $index')),
nextChild
: _state.asMap().containsKey(index + 1) ?
buildContainer(index + 1) : const SizedBox()
}
return buildContainer(0);
});
This seems to not cause the rebuilds but I am concerned about the use of functions as this is discouraged in the docs.
Also it makes passing actual content to these children for more involved as technically all potential children (that are actually page routes contained in a Map). Each NestContainer would need to receive the whole Map so that it can render the correct item from it which means passing a lot of unused data to every single NestContainer.
What is the best approach for dealing with this matter?
Solution 1:[1]
Keys are important. They allow flutter to look at a stack of widgets as a whole and determine whether anything inside that stack has changed.
List _state = Provider.of<Somesuch>(context).routes;
for(int i = 0; i < _state.length; i ++)
Container(key: ValueKey('Container $I'), child: Text('Item $I'),
The addition of that key means that if that container does not change and it's place in the list does not change none of it will be checked for a change.
However, this is not the full answer. Memory usage is based around a number of rendered widgets on screen among other things. Getting rid of anything not on screen is important.
Many will say that the best way is to remove unused widgets from the tree. ie
Stack(children:[
someStatefield ? SomeWidget() : SizedBox()
])
This is true but it is not always possible particularly in a complex navigator. The idea is instead to replace widgets that can't be seen on screen with the minus amount of content possible until they are needed to be seen again.
The easiest way is to use the Visibility class. With Visibility(visible: false), the child of this class will be replaced with a SizedBox and it's content restored when set to true. Adding maintainState: true will allow it to maintain responses to any other state changes without rendering any content or performing any anumations until it's visible again.
Visibility(
visible: (true or false based on your state),
maintainState: true // ensures the child still responds to changes in state despite rendering no content or animations
child: // Your Widgets and content that will be replaced by a SizedBox when not visible
)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | RIK |
