| name | flutter-devtools-perf |
| description | Use Flutter DevTools Performance, CPU, Memory, and Network views to find UI-thread and raster-thread bottlenecks. |
Flutter DevTools Performance
Instructions
Flutter DevTools is the canonical profiler for Flutter apps on iOS, Android, desktop, and web. Run it against a profile-mode build for real numbers; debug mode is up to 10× slower and obscures real bottlenecks.
1. Launch
flutter pub global activate devtools
flutter run --profile
# DevTools URL printed in the run output; open in Chrome
Or from VS Code: Command Palette → Dart: Open DevTools.
2. Performance View — Frames and Timelines
- Frames chart at the top: bars per frame, colored red when over budget.
- Click a red frame — the Timeline panel shows two flame charts:
- UI (Dart thread) — widget build, layout, paint.
- Raster (GPU thread) — Skia/Impeller rasterization.
- Enhance Tracing:
- Track Widget Builds — each build event appears as a slice.
- Track Layouts — render-object layout slices.
- Track Paints — paint slices.
- Track Platform Channels — latency of method calls to native.
Reading pattern:
- If UI flame is wide → optimize Dart: reduce rebuilds, move work off UI isolate.
- If Raster flame is wide → overdraw, blurs, or shader compile.
- Gap between UI end and Raster start → platform / vsync issue.
3. CPU Profiler
Tab: CPU Profiler. Click Record, perform the action, stop.
- Call Tree → top-down.
- Bottom Up → hot leaves aggregated.
- Flame Chart → visual hierarchy.
Filter to the UI isolate. Use VM Tags (e.g., CompileUnoptimized, Runtime) to find compile jank versus steady state.
4. Memory View
- Chart: Used / Capacity / External memory over time.
- GC events plotted as dots.
- Heap Snapshot: click "Snapshot" → see class-by-class instance counts.
- Diff Snapshots: take before + after navigation round-trip; non-zero
retained counts reveal leaks.
Filter by type to check your State and controller classes.
For CI, use the leak_tracker package:
void main() {
LeakTracking.start();
runApp(const MyApp());
}
5. Widget Inspector — Rebuild Stats
DevTools → Widget Inspector → More → Rebuild Stats.
- Counts rebuilds per widget type on every frame.
- Sort descending. The first five are your targets.
- Common offenders:
- A top-level
Consumer/BlocBuilder wrapping the whole screen.
- Forgotten
const constructors.
- New lambdas passed down each rebuild (use stable references /
useCallback via hooks).
6. Network View
Shows Dio / http client requests:
- Bytes in/out, duration, HTTP status.
- Timing breakdown: DNS, TLS, waiting, receive.
Use to confirm caching (302 from cache) and to spot request storms.
7. Provider View
Riverpod and Provider expose their state here. Useful to see when state changes and which widgets listen.
8. Shader Compilation Jank
A unique Flutter problem on Android: the first animation after install compiles shaders, causing visible jank.
Fix:
-
Use Impeller (default on iOS, enabled on Android by default in recent stable). Impeller pre-compiles shader variants.
-
For legacy Skia: generate SkSL warm-up:
flutter run --profile --cache-sksl --purge-persistent-cache
# reproduce animations you care about
# quit; the SkSL is saved. Build:
flutter build apk --bundle-sksl-path flutter_01.sksl.json
9. Impeller Diagnostics
If using Impeller, enable:
flutter run --profile --dart-define=IMPELLER_ENABLE_VALIDATION=1
Validation logs mis-uses that cause unexpected GPU work.
10. Production Metrics
Use dart:developer's Timeline in your code:
Timeline.startSync('loadFeed');
final feed = await api.fetchFeed();
Timeline.finishSync();
Ship a production profiler like SentryPerformance or Firebase Performance for field data.
11. Common Flutter Wins
const constructors everywhere possible.
- Extract widgets so only the leaf rebuilds.
ListView.builder + itemExtent for uniform rows.
RepaintBoundary around an animated subtree inside a larger static screen.
- Move JSON parsing to isolates with
compute() or Isolate.run.
- Precache images with
precacheImage before the frame that needs them.
Checklist