In my Flutter app, I use an AnimatedList, with 100 items, and only a few can be displayed at once on the screen.
When I first run it, I can see in the logs that only the necessary items are built. But if I refresh the content of my list, for some reason, the AnimatedList rebuilds every item, which makes my app laggy.
Here is the code :
class _MyHomePageState extends State<MyHomePage> {
static const _durationMin = Duration(microseconds: 1);
final List<String> _items = List.generate(100, (index) => "Item at $index");
final GlobalKey<AnimatedListState> _listKey = GlobalKey();
late ScrollController _scrollController;
@override
void initState() {
super.initState();
_scrollController = ScrollController(initialScrollOffset: 0);
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
void _onUpdate() {
_listKey.currentState?.removeAllItems((_, __) => const SizedBox.shrink(), duration: _durationMin);
_listKey.currentState?.insertAllItems(0, _items.length, duration: _durationMin);
}
Widget _buildItemWidget(String text) {
return ListTile(
title: Text(text),
trailing: IconButton(
icon: const Icon(Icons.remove),
onPressed: () {},
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: SafeArea(
child: AnimatedList(
key: _listKey,
padding: EdgeInsets.zero,
controller: _scrollController,
initialItemCount: _items.length,
itemBuilder: (context, index, animation)
{
print("index : $index");
return _buildItemWidget(_items[index]);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: _onUpdate,
child: const Icon(Icons.refresh),
),
);
}
}
So when I run the app, here is what I see in the logs:
index : 0
...
index : 18
But if I press the floating action button, here is what I see in the logs:
index : 0
...
index : 99
Why do I have that behavior? How can I fix that so when I press the floating action button, it only rebuilds the necessary items?
Thanks for your help
If you see the documentation of
removeAllItems, it says:Which means the
SizedBox.shrink()widget will remains for_durationMin, but you immediately calledinsertAllItemsafter so that the builder will build all 100 items with theSizedBox.shrink()widget.SizedBox.shrink()has a height of 0, which means all of them are visible on the screen so they are all built.There are some scenarios you can try:
removeAllItemstoDuration.zero, you can see it will work as expected (items are built only until index 18). This is because at the timeinsertAllItemsis called, the widgets built are thoseListTilewhich has actual height.SizedBox.shrink()withText('test'), you will see the number of widgets built are decreased (not until index 99, but only until index 51, in my case), because now there are less widgets visible on the screen.Note: When I say "visible on the screen", it also includes buffered items that comes after the last visible item.