What causes a widget to rebuild, and how do you avoid unnecessary rebuilds?
Show answer — try answering out loud first
A widget rebuilds when its parent rebuilds, when you call setState, or when an InheritedWidget it depends on changes. To avoid unnecessary rebuilds: use const constructors, extract subtrees into separate widgets, keep the scope of setState as small as possible, and use the child parameter (which is not rebuilt) in widgets like AnimatedBuilder.
build doesn't paint pixels directly; it produces the description of the tree. But if you rebuild too often and too much, you waste CPU and can cause jank (stutters in animations). The causes of a rebuild:
- The parent rebuilds: by default it drags its children along.
setState: rebuilds theStatethat called it.- An
InheritedWidgetthe widget depends on changes (for example,Theme).
Strategies to reduce them:
const: subtrees that don't change aren't rebuilt.- Extract widgets: move the part that changes into its own small widget, so only that one rebuilds.
- Scope
setState: keepsetStatein the smallest possible widget, not in the whole screen. childparameter: inAnimatedBuilder/ValueListenableBuilder, thechildis built once and passed through without rebuilding.
import 'package:flutter/material.dart';
// The "child" trick: the expensive part (the Text) is built ONCE
// and is not rebuilt on every frame of the animation.
class Pulsing extends StatelessWidget {
const Pulsing({super.key, required this.animation});
final Animation<double> animation;
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: animation,
// This child is NOT rebuilt on every frame:
child: const Text("I don't rebuild every frame"),
builder: (context, child) {
return Opacity(
opacity: animation.value,
child: child, // reuse the child built only once
);
},
);
}
}
Calling setState at the highest widget (for example, on the entire screen) for a change that only affects a tiny button. That rebuilds half the UI when rebuilding just that button would be enough. It's also common to skip const and cause avoidable rebuilds on every frame. To diagnose, use the Flutter DevTools (the Performance view and the rebuild counter).
"A widget rebuilds when its parent does, when I call
setState, or when anInheritedWidgetit depends on changes, likeTheme. To avoid unnecessary rebuilds I useconstconstructors, extract the part that changes into its own small widget to scope the rebuild, putsetStatein the most specific widget possible, and take advantage of thechildparameter ofAnimatedBuilderso I don't rebuild what doesn't change. When something is slow, I measure it with DevTools before optimizing blindly."
What is the child parameter of an AnimatedBuilder for?
See answer
It lets you build the part of the subtree that does not depend on the animation only once, and reuse it on every frame. The builder runs on every frame of the animation, but the child is passed in already built, so you avoid rebuilding that portion 60 times per second. It's a key optimization in animations.