DevTools and performance
Finding out why an app is slow instead of guessing. DevTools is the primary instrument; the second half of this page is the catalogue of problems it usually reveals.
Contents
- Profile mode
- Opening DevTools
- Flutter Inspector
- Performance view
- Rebuild tracking
- CPU profiler
- Memory view
- Network view
- Debug flags
- App size
- Common causes of jank
Profile mode
Debug builds are not representative — assertions run, the code is JIT-compiled, and everything is slower. Never draw a performance conclusion from a debug build.
flutter run --profile
Profile mode compiles ahead of time like release but keeps the instrumentation DevTools needs. Use a physical device; emulators have entirely different GPU characteristics.
Opening DevTools
- From
flutter run: pressv, or open the URL printed in the console. - From VS Code: Dart: Open DevTools.
- From Android Studio: the Open DevTools action in the run toolbar.
Flutter Inspector
Shows the widget tree as it exists on the device.
- Select widget mode — tap something on the device and jump to it in the tree and the source.
- Details tree — the properties of the selected widget, including the constraints it received and the size it chose. This answers most layout questions directly.
- Layout Explorer — a visual editor for
RowandColumn: flex factors, alignment, and overflow, changeable live so you can find the right values before writing them.
Performance view
The frame chart is the core tool. Each bar is a frame, split into two parts:
| Bar | Thread | Too tall means |
|---|---|---|
| Blue | UI (Dart) | Build, layout, or your own code is too slow |
| Green | Raster | Painting and GPU work is too slow |
Bars above the budget line are janked frames. Click one to open the timeline events for that frame and see exactly which phase consumed the time.
Add your own markers to the timeline for code you suspect:
Timeline.timeSync('parse response', () {
_products = parseProducts(body);
});
Enhanced tracing toggles add per-widget-build, per-layout, and per-paint events. They add overhead themselves, so turn them on to find the culprit and off to measure the fix.
Rebuild tracking
Track widget builds counts rebuilds per widget per frame and shows the counts next to your source. A widget rebuilding sixty times a second when nothing about it changed is the most common and most fixable performance bug in Flutter.
Typical fixes, in order of effectiveness:
- Add
constconstructors so the framework can skip the subtree entirely. - Extract widgets into classes instead of
_build...()helper methods. - Narrow the subscription:
context.select,Consumer,ValueListenableBuilderaround the smallest possible subtree. - Pass the static subtree as the
childargument ofAnimatedBuilder,Consumer, orListenableBuilder.
See State management and Rendering pipeline.
CPU profiler
Samples the Dart isolate and shows where time is spent, as a flame chart, call tree, or bottom-up view.
Use it when the UI thread is the problem. Record while reproducing the slow interaction, then read the bottom-up view to find the function with the most self time. Long synchronous work found here is a candidate for an isolate — see Async and isolates.
Memory view
- Chart — heap over time, with garbage-collection events.
- Heap snapshot — every live object by class, with retaining paths.
- Diff two snapshots — the reliable way to find a leak.
The standard leak test: snapshot, navigate into a screen and back five times,
force a garbage collection, snapshot again, diff. Any State, controller, or
bloc that accumulates is leaking, almost always because something was not
disposed or a subscription was not cancelled — see Lifecycle.
Images usually dominate the heap. Check ImageCache size and confirm you are
decoding at display resolution rather than source resolution.
Network view
Records HTTP traffic from dart:io and package:http, with timing, headers,
and bodies. Useful for two questions that look like UI problems: how many
requests a screen actually issues (usually more than intended), and how much of
"slow rendering" is really waiting on the network.
Debug flags
Set these temporarily in main or from the Inspector:
import 'package:flutter/rendering.dart';
void main() {
debugPaintSizeEnabled = true; // layout bounds
debugRepaintRainbowEnabled = true; // colour changes on repaint
debugPaintLayerBordersEnabled = true;
debugProfileBuildsEnabled = true;
runApp(const MyApp());
}
debugRepaintRainbowEnabled is the fastest way to find unnecessary repaints: if
a large region flashes colour while only a small part of the screen is
animating, add a RepaintBoundary around the animating part.
App size
flutter build apk --analyze-size --target-platform android-arm64
flutter build ipa --analyze-size
Opens a treemap of what is in the binary. Usual offenders: uncompressed images, unused fonts (especially full icon fonts), bundled models and videos, and large transitive dependencies. Also check that debug assets and test fixtures did not end up in the bundle.
Common causes of jank
| Symptom | Cause | Fix |
|---|---|---|
| Tall blue bars | Too many rebuilds | const, extracted widgets, narrower subscriptions |
| Tall blue bars, one slow function | Synchronous work on the UI thread | Move to an isolate, or chunk it |
| Tall green bars | Expensive painting | Remove saveLayer triggers, add repaint boundaries |
| Jank while scrolling a list | Building all children | ListView.builder, itemExtent, avoid shrinkWrap |
| Jank when images appear | Decoding full-resolution images | cacheWidth/cacheHeight, ResizeImage, precache |
| Memory climbing on navigation | Undisposed controllers or subscriptions | Dispose in dispose, verify with a heap diff |
| First animation stutters | Shader or pipeline warm-up | Verify on Impeller; measure the first run of the route specifically |
| Slow start-up | Work in main or the first build | Defer to after the first frame with addPostFrameCallback |
| Everything slow only in debug | Debug mode | Re-measure in profile mode |
A discipline that saves time: fix one thing, re-measure, and keep the numbers. Performance work without a before-and-after is indistinguishable from superstition.
Next: Testing.