flutter_debug_tracker: in-app debug tool for Flutter (Android & iOS)
![]()
flutter_debug_tracker is an in-app debugger for Flutter. A floating debug bubble sits over your app and opens a full debug panel, on the phone, without DevTools or a cable. It combines a performance monitor (FPS, jank, frame time, memory), a network inspector for HTTP requests, a logger with crash capture, a Firebase Analytics event viewer, route history, device info, a file browser and UI debugging tools.
It is inspired by DebugSwift for iOS and works on Android and iOS. Pure Dart: no native code and no third-party dependencies. It is off by default in release builds.
- Screenshots
- Features
- Installation
- Quick start
- Firebase Analytics debugging
- Configuration
- Performance overhead
- FAQ
- Limitations
Screenshots
| Debug bubble + HUD | Performance monitor | Network inspector |
|---|---|---|
| Request details | Firebase Analytics events | Logs and crashes |
|---|---|---|
UI tools
Features
Performance monitor: FPS, jank and memory
- Live FPS in the bubble, color-coded against the display refresh rate.
- UI (build) and raster time per frame, a frame-time chart with the frame budget, the jank count and rate, and the worst frame.
- Memory (RSS) and peak memory with a 2-minute history chart.
- A compact on-screen performance HUD, toggled by long-pressing the bubble.
- Custom traces that time your own work, such as API calls, database loads or screen builds.
Network inspector: HTTP request logger
- Captures every
dart:ioHttpClientrequest. This coverspackage:httpand Dio's default adapter, with no interceptor to add. - Shows the method, URL, status, duration and size, request and response headers, pretty-printed JSON bodies, and errors.
- Search, an errors-only filter, and copy as cURL.
Logs and crash reporting
- Captures
print,debugPrint,DebugTracker.log,FlutterErrorand uncaught async errors, with stack traces. - Filter by level and search. The bubble shows an error badge.
Firebase Analytics event viewer
- Every
FirebaseAnalytics.instance.logEvent(andlogScreenView,logPurchase, ...), plussetUserId,setUserProperty, consent and default parameters, captured automatically. - Events from other analytics SDKs (Mixpanel, Amplitude, your own
backend) via
DebugTracker.logAnalyticsEvent.
More tools
- Routes: the current navigator stack and push/pop history.
- App and device info: OS version, locale, CPU cores, build mode, screen size, pixel ratio, refresh rate, safe area, plus your own fields (flavor, user id, ...).
- File browser: read-only view of the app sandbox (
Documents,Libraryandtmpon iOS;files,cache,shared_prefsanddatabaseson Android). - UI tools: Flutter's performance overlay, an 8 pt grid, touch indicators, slow animations, layout bounds and repaint rainbow.
Installation
flutter pub add flutter_debug_tracker
Requires Flutter 3.38 or newer.
Quick start
import 'package:flutter/material.dart';
import 'package:flutter_debug_tracker/flutter_debug_tracker.dart';
void main() {
// Starts every tracker and captures print() inside the zone.
// Do not call WidgetsFlutterBinding.ensureInitialized() before this;
// call it inside the callback if you need it.
DebugTracker.runZoned(() => runApp(const MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorObservers: [DebugTracker.observer],
builder: DebugTracker.builder(),
home: const HomePage(),
);
}
}
Run the app and tap the bubble.
| Gesture | Action |
|---|---|
| Tap the bubble | Open the debug panel |
| Long-press the bubble | Toggle the performance HUD |
| Drag the bubble | Move it; it snaps to the nearest edge |
| System back or back swipe | Close a panel page, then the panel |
Already using MaterialApp.builder? Pass it through:
builder: DebugTracker.builder((context, child) => MyWrapper(child: child!)),
CupertinoApp and WidgetsApp work the same way.
Logs and custom traces
DebugTracker.log('User signed in', tag: 'auth');
DebugTracker.log('Token expires soon', level: LogLevel.warning);
final feed = await DebugTracker.trace('load feed', () => api.loadFeed());
Programmatic control
DebugTracker.open(); // open the panel
DebugTracker.close(); // close it
DebugTracker.instance.bubbleVisible.value = false; // hide the bubble
DebugTracker.instance.hudVisible.value = true; // show the HUD
DebugTracker.stop(); // remove every hook
Firebase Analytics debugging
You don't need DebugView or adb shell setprop debug.firebase.analytics.app.
Start the app with DebugTracker.runZoned and keep calling Firebase as
usual:
void main() {
DebugTracker.runZoned(() async {
WidgetsFlutterBinding.ensureInitialized(); // fine inside runZoned
await Firebase.initializeApp();
runApp(const MyApp());
});
}
// Anywhere in the app: shows up in the Analytics tab right away.
await FirebaseAnalytics.instance.logEvent(
name: 'add_to_cart',
parameters: {'item_id': 'sku-42', 'price': 19.99},
);
How it works: the tracker installs a DebugTrackerBinding whose platform
messenger reads the messages firebase_analytics sends to the native
Firebase SDK. You therefore see exactly what Firebase receives, and this
package does not depend on firebase_analytics.
For other providers:
DebugTracker.logAnalyticsEvent(
'checkout_started',
provider: 'mixpanel',
parameters: {'cart_value': 42.5},
);
Configuration
DebugTracker.runZoned(
() => runApp(const MyApp()),
config: DebugTrackerConfig(
enabled: !kReleaseMode, // default: off in release builds
trackPerformance: true,
trackNetwork: true,
trackLogs: true,
trackErrors: true,
trackAnalytics: true,
showPerformanceHud: false,
maxNetworkEntries: 200,
maxLogEntries: 1000,
maxAnalyticsEvents: 500,
maxBodyBytes: 64 * 1024,
networkIgnore: (uri) => uri.host.contains('crashlytics'),
customInfo: {'Flavor': 'staging', 'User': '42'},
),
);
To ship the tracker in an internal QA build, pass enabled: true behind
your own flag.
Performance overhead
A debug tool should not slow down the app it measures:
- Frame data comes from
SchedulerBinding.addTimingsCallback, which the engine already batches. No per-frame callback or ticker is added. - Metrics and memory are sampled once per second.
- Captured data lives in fixed-size ring buffers, and request/response bodies are capped, so memory use does not grow with uptime. Binary bodies (images, video, fonts) are never stored.
- List updates are coalesced, and panel widgets are only built while the panel is open.
- The bubble only repaints when the shown FPS changes, so an idle app stays idle.
- With
enabled: false(the release default), no hook is installed.
FPS counts frames Flutter actually rendered in the last second. Flutter
only renders when something changes, so an idle screen shows 0 FPS. For
real numbers, run in profile mode (flutter run --profile).
FAQ
How do I inspect network requests in a Flutter app on a real device?
Add DebugTracker.builder() and start with DebugTracker.runZoned. Every
HttpClient, http and Dio request then appears in the Network tab with
headers, body and timing. You don't need a proxy or a certificate.
How can I see Firebase Analytics events while developing?
Open the Analytics tab. Each logEvent, user property and user id call
appears immediately with its parameters. There is no DebugView delay.
How do I measure FPS and jank in Flutter? The bubble shows live FPS. The Performance tab shows UI and raster time per frame, the jank rate and memory. Long-press the bubble for an always-on HUD.
Does it affect my release build? No. It is disabled in release mode by default, and the overlay then renders only your app.
Does it work with Dio, GetX, Riverpod or BLoC? Yes. Network capture works below Dio's default adapter, and the overlay is a plain widget, so it works with any state management.
Limitations
- Network capture covers
dart:ioHttpClient. Clients that bypass it (cupertino_http,cronet_http, native SDKs, WebViews) are not captured. If another library setsHttpOverrides.globalafter the tracker starts, wrap its clients withDebugTracker.instance.network.wrap(client). printand Firebase Analytics are captured automatically only when the app starts withDebugTracker.runZoned.- Analytics events logged by native code (not through Dart) are not captured.
- "Hide bubble" in the Tools tab hides it until you set
DebugTracker.instance.bubbleVisible.value = trueor restart the app. - Supported platforms: Android and iOS.
Example
See example/
for a demo app that generates network traffic, logs, errors, jank, traces
and analytics events.
License
MIT. See LICENSE.
Libraries
- flutter_debug_tracker
- In-app debug overlay for Flutter apps on Android and iOS.