Expert · Animations and performance · topic 38 of 50 · 35 min
Performance
Explanation
The frame budget
Flutter draws the screen as frames. At 60 frames per second, each frame has about 16 milliseconds; on 90 or 120 Hz phones, even less. If building, laying out and painting a frame takes longer, the frame is dropped, and the user sees stutter (jank), especially while scrolling or animating. Budget Android phones, common in India, have slower CPUs, so test on one if you can.
Measure first, in profile mode
Debug mode is slow on purpose (it supports hot reload and many checks), so never judge performance in debug. Run flutter run --profile on a real device: it is almost as fast as release but keeps the profiling tools. Open Flutter DevTools: the Performance view shows frame timings (red bars are slow frames), and the widget rebuild tracker shows what rebuilds too often.
flutter run --profile # measure here, on a real phone
flutter run --release # what users getconst widgets
A const widget is created once at compile time and reused. When the parent rebuilds, Flutter sees the identical const widget and skips rebuilding it. Mark widgets const wherever their arguments are constant: const Text('Hello'), const SizedBox(height: 8), const Icon(Icons.add). The analyser (with flutter_lints) suggests where const is possible.
Smaller rebuilds
setState rebuilds the whole State's build method. If a big screen has one ticking timer, extracting the timer into its own small StatefulWidget means only that small widget rebuilds every second. The same idea applies to Provider's Consumer/select and Riverpod's narrow watches: rebuild as little as possible.
// Instead of setState in the whole page every second:
class CountdownText extends StatefulWidget {
const CountdownText({super.key});
@override
State<CountdownText> createState() => _CountdownTextState();
}
class _CountdownTextState extends State<CountdownText> {
int _seconds = 300;
Timer? _timer;
@override
void initState() {
super.initState();
_timer = Timer.periodic(const Duration(seconds: 1), (_) => setState(() => _seconds--));
}
@override
void dispose() { _timer?.cancel(); super.dispose(); }
@override
Widget build(BuildContext context) => Text('OTP expires in $_seconds s');
}Keys
Keys help Flutter match widgets to their state when items are added, removed or reordered in a list. Use ValueKey(item.id) for items with an id, ObjectKey for objects, and UniqueKey to force a fresh widget. GlobalKey gives access to a widget's state from elsewhere (like FormState) but is expensive; use it only when needed.
Lists and images
- Use ListView.builder and GridView.builder for long lists (module 6).
- Images: a 4000 x 3000 photo shown at 100 x 100 still decodes the full image into memory. Use Image(cacheWidth: ...) or request smaller images from the server.
- Cache network images (cached_network_image) so scrolling back does not re-download.
- Move heavy computation off the main isolate with Isolate.run or compute (module 3).
RepaintBoundary, briefly
RepaintBoundary isolates a part of the screen that repaints often, like an animation, so the rest does not repaint with it. Flutter already adds them in many places (for example around list items); add your own only when DevTools shows excessive repainting.
Common mistakes
- Judging speed in debug mode.
- Optimising without measuring: wasted time on parts that were already fast.
- Heavy work in build (sorting, parsing, formatting a whole list) on every rebuild.
- Using GlobalKeys and big setState calls everywhere.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Performance · 1/6
Each frame has about sixteen milliseconds at sixty frames per second. Missed frames cause jank.
Prefer a video by a teacher? Find free videos on “flutter performance tips const profile mode devtools jank rebuilds” on YouTube ↗
In real life: Smoothing a laggy product feed
A shopping app's home feed stuttered on budget phones. Profiling in profile mode showed full-size product photos being decoded for small thumbnails and the whole page rebuilding every second because of a countdown banner. The team requested 400-pixel images from the CDN, set cacheWidth, moved the countdown into its own widget and added const where the analyser suggested. Scrolling became smooth on a ₹9,000 phone.
Image.network(
product.thumbnailUrl,
width: 120,
height: 120,
cacheWidth: 240, // decode at about 2x display size, not full resolution
fit: BoxFit.cover,
)Practice (0/3 done)
Which mode should you use to measure performance?
A page with a 1-second countdown rebuilds everything every second. Best fix?
Take a list-heavy screen from your app. Run it in profile mode on a real phone with DevTools open, record the frame chart while scrolling, then apply three fixes (const widgets, smaller rebuild scope, cacheWidth or builder lists) and record again. Note the difference in slow frames.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Explicit animationsAnimations and performance · same module
- Implicit animationsAnimations and performance · same module
- Interview questions: FlutterFlutter careers and interviews
- Debugging and performanceKotlin and Android App Development · Testing Android apps
- Performance and Core Web VitalsFrontend Web Development · Fast, accessible, findable and safe: quality for real sites
- Browser DevTools: look inside any pageFrontend Web Development · How the web works
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
Why does const help performance?
Answer: Const widgets are created once and can be skipped during rebuilds
Identical const widgets are reused.
What does cacheWidth on an Image do?
Answer: Decodes the image at a smaller size to save memory
Avoids decoding full resolution for thumbnails.
Which key suits list items that have database ids?
Answer: ValueKey(item.id)
Stable identity from data.
Why is debug mode not good for judging speed?
Answer: It adds checks and hot reload support that slow it down
Use profile mode.
Which object holds a StatefulWidget's State between rebuilds?Related
What are Baseline Profiles for?Related
Answer: Improving start-up and early frame performance through ahead-of-time compilation of key paths
They ship hints to compile hot code early.
What is a widget?Related
Answer: An immutable description of part of the UI
Flutter rebuilds the screen from widgets.
Why should the hero image NOT use loading="lazy"?Related
Answer: It is usually the LCP element, and lazy loading makes the browser wait before fetching it
Lazy loading is for images far down the page. On the hero it delays the largest paint and hurts LCP.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- This is the last topic of “Animations and performance”. The module exam comes next.
- Module 9 exam: Animations and performance · 12 questions · 25 min, without help. 4 topics of this module still to finish first.
- Then the next module, Responsive design and testing. 50 topics left in the course before the final exam.