Flutter DevTools: profiling your app step by step
By John · 11 February 2025
Flutter DevTools is the official debugging and performance suite for Flutter. It runs in the browser and connects to a running Flutter app via VM service. Most developers underuse it — it's the fastest way to diagnose jank, memory leaks, and widget tree bloat.
Opening DevTools
flutter run
# DevTools URL is printed:
# http://127.0.0.1:8181/...
In VS Code: F5 to run, then open DevTools via the Flutter sidebar. In Android Studio: View → Tool Windows → Flutter Inspector.
Performance tab: finding jank
Jank occurs when frames take longer than 16.67ms (60 FPS). The Performance tab shows a frame timeline.
- Trigger the janky scroll/animation
- Click Record
- Reproduce the jank
- Click Stop → see the frame timeline
Red frames = over budget. Click a red frame for its flame chart. Wide blocks = lots of time. Look for build, layout, and paint taking > 2ms each.
Widget Inspector
Shows the entire widget tree of the running app.
Find unnecessary rebuilds: Select "Track widget builds" then interact with the app. Rebuilt widgets highlight. If Column or Container rebuilds every frame, you're missing const constructors or RepaintBoundary.
Inspect a widget: Tap "Select Widget Mode" then click a widget in the app. The right panel shows render properties, constraints, and size.
Memory tab: finding leaks
Common sources of memory leaks:
- Animation controllers not disposed
- Stream subscriptions not cancelled
ScrollControllernot disposed
Workflow:
- Memory tab → click Monitor
- Navigate through screens
- Navigate back to start
- Click GC → take a heap snapshot
If memory grows after returning to the starting screen, you have a leak. Search the snapshot for your widget's state class name.
Fix:
class _MyWidgetState extends State<MyWidget> with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: const Duration(seconds: 1));
}
@override
void dispose() {
_controller.dispose(); // REQUIRED
super.dispose();
}
}
Network tab
Shows all HTTP requests (works with Dio automatically). Use it to:
- Inspect request/response bodies and headers
- Find slow requests
- Debug auth header issues
Logging tab
Shows print(), debugPrint(), and framework log messages. Better than the terminal — filter by level, search, and see timestamps.
App Size analysis
flutter build apk --analyze-size
flutter build ios --analyze-size
Loads into DevTools → App Size as a treemap. Common findings: Firebase and Google Maps are large. The Flutter engine itself is ~5-7MB and unavoidable.
CPU Profiler
For non-UI performance (slow JSON parsing, compute-heavy operations):
- CPU Profiler → Start recording
- Trigger the slow operation
- Stop → see where time was spent
Heavy processing on the main thread causes jank. Move it to an isolate:
final result = await compute(parseOrders, rawJson);
Always profile in profile mode
Debug mode is 2-5x slower. Always use --profile for performance work:
flutter run --profile
Performance overlay
For quick in-app monitoring:
MaterialApp(
showPerformanceOverlay: true,
)
RepaintBoundary
Animating one widget repaints its entire parent subtree. Wrap animated widgets to isolate repaints:
RepaintBoundary(
child: AnimatedWidget(...),
)
Common pitfalls
Profiling in debug mode. Debug overhead masks real performance costs. Always use --profile.
Ignoring the Widget Inspector. It shows which widgets rebuild and why — essential for performance, not just layout debugging.
Not setting up a RepaintBoundary. Without it, a 60 FPS animation can trigger expensive repaints of large parts of the tree on every frame.
Sign in to like, dislike, or report.