Flutter2 min read
Keeping Flutter rebuilds cheap
Three habits that stop a Flutter screen from rebuilding more than it needs to: const widgets, small widgets, and lazy lists.
By IntoCoder
Flutter rebuilds widgets often, and that is by design. Building a widget is cheap. The trouble starts when one small change rebuilds a whole screen, sixty times a second. Three habits prevent most of it.
1. Use const wherever you can
A const widget is created once and reused. When its parent rebuilds, Flutter sees the identical instance and skips that subtree.
// Rebuilt every time the parent builds
Padding(padding: EdgeInsets.all(16), child: Text('Settings'));
// Created once, skipped on rebuild
const Padding(padding: EdgeInsets.all(16), child: Text('Settings'));
Turn on the prefer_const_constructors lint and let the analyzer find these for you.
2. Push state down into small widgets
setState rebuilds the widget it is called in, and everything below it. If a counter lives in the state of an entire screen, tapping it rebuilds the app bar, the list, and the footer too.
Move the changing part into its own widget:
class LikeButton extends StatefulWidget {
const LikeButton({super.key});
@override
State<LikeButton> createState() => _LikeButtonState();
}
class _LikeButtonState extends State<LikeButton> {
bool liked = false;
@override
Widget build(BuildContext context) {
return IconButton(
icon: Icon(liked ? Icons.favorite : Icons.favorite_border),
onPressed: () => setState(() => liked = !liked),
);
}
}
Now a tap rebuilds one icon button. Prefer extracting widgets into classes over helper methods that return widgets: a class gets its own element and can be skipped, while a method is simply re-run with its parent.
For values shared across the tree, ValueListenableBuilder does the same job. Only the builder rebuilds when the value changes.
3. Build lists lazily
ListView(children: [...]) builds every child up front, visible or not. ListView.builder builds only what is on screen, plus a small buffer:
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) => ItemTile(item: items[index]),
);
For a list of a few hundred rows this is the difference between a smooth scroll and a dropped frame on open.
Measure before you optimize
Open Flutter DevTools and run the app in profile mode, not debug mode; debug builds are much slower and will mislead you. The performance view shows which frames ran long and whether the time went to building or painting. Fix what the profile shows, then measure again.