flutter_route_inspector 1.0.6
flutter_route_inspector: ^1.0.6 copied to clipboard
Universal navigation debugger: route timeline, screen time, duplicate-push hints, exports, and a draggable overlay. Works with Navigator 1/2, GetX, GoRouter, and AutoRoute.
flutter_route_inspector #
Universal navigation debugger for Flutter — not a router. Plug in observers, optionally show a draggable overlay, and get route intelligence without migrating routing code. Use it in debug builds, QA, or demos to see what your navigator is doing in real time.
Screenshots #
Optional floating overlay with recent route transitions (animated):

Collapsed HUD: current route and optional tab in the header bar.

Route history with a duplicate-push hint when the same label is stacked twice.

Dismiss the overlay: long-press, then drag the bubble into the bottom strip.

Asset files on GitHub: route_overlay.gif · collapsed_floating.png · route_history_with_duplicate_route.png · drag_to_dismiss.png
Why use it? #
- Zero router lock-in — works wherever a Flutter
Navigatorruns (Material, Cupertino, GetX, GoRouter, AutoRoute, nested navigators, and more). - Human-readable labels — named routes, map arguments, or the real page
widget type for anonymous
MaterialPageRoute/CupertinoPageRoute/PageRouteBuilderwhen the observer has a host context. - Actionable timeline — push / pop / remove / replace, optional screen time per route, and duplicate-push warnings when the same label is stacked twice.
- Ship-friendly workflow — export logs as plain text or JSON Lines, copy to
clipboard from the HUD, or bind
debugStoreto your own debug panel.
Installation #
Add to pubspec.yaml:
dependencies:
flutter_route_inspector: ^1.0.6
Or:
flutter pub add flutter_route_inspector
SDK: Dart >=3.0.0 <4.0.0, Flutter >=3.10.0.
Features #
- Auto-detect current screen via the same
Navigatorstack your app already uses - Readable route labels —
RouteSettings.name, commonargumentskeys, then inner page widget type for standard modal routes (see Route naming), then sanitized route runtime type, then a stable anonymous fallback - Optional floating overlay (draggable) with recent transitions; at most one overlay entry so the HUD never stacks on itself
- Route transition history with push / pop / remove / replace
- Time on screen while a route was top-most (push-away or pop)
- Duplicate push hints when the same resolved label is pushed on top of itself
- Tab / segment context via
reportTabChangewhen bottom navigation does not push routes - Export navigation logs as plain text or JSON Lines; clipboard export from the HUD
- Works with Navigator 1.0, Navigator 2.0 /
Router, GetMaterialApp, GoRouter (observers), AutoRoute (navigator observers on your app router), and any package that ultimately drives a FlutterNavigator
Non-goals #
This package does not own routing, deep links, or route state. It observes transitions only.
Setup #
1. Initialize (typically in main) #
import 'package:flutter_route_inspector/flutter_route_inspector.dart';
void main() {
FlutterRouteInspector.initialize(
showOverlay: true,
showScreenTimeOnOverlay: false, // optional: hide [screen: …s] in overlay list
debugPrintLogs: true,
);
runApp(const MyApp());
}
initialize options
| Parameter | Default | Purpose |
|---|---|---|
showOverlay |
false |
When true, installs the draggable HUD on the navigator overlay after the first frame |
showScreenTimeOnOverlay |
true |
When false, hides [screen: …s] segments in the overlay list only (exports and logs unchanged) |
navigatorKeyOverride |
null |
Use your own GlobalKey<NavigatorState> instead of the package default |
debugPrintLogs |
true |
Print [ROUTE …] lines via debugPrint |
onLog |
null |
Optional sink for every log line (analytics, file, remote, etc.) |
maxHistoryEvents |
500 |
Cap in-memory timeline length (store is recreated if the cap changes) |
onlyPrimaryNavigator |
true |
Ignore routes whose navigator is not the keyed primary navigator |
clearHistoryOnInitialize |
false |
Clear the in-memory store when re-initializing with the same maxHistoryEvents |
2. Wire the navigator key and observers #
MaterialApp(
navigatorKey: FlutterRouteInspector.navigatorKey,
navigatorObservers: [
...FlutterRouteInspector.navigatorObservers,
// ...your existing observers
],
home: const Home(),
);
For GoRouter:
final goRouter = GoRouter(
observers: [
...FlutterRouteInspector.navigatorObservers,
],
routes: [
// ...
],
);
For GetX (GetMaterialApp):
GetMaterialApp(
navigatorKey: FlutterRouteInspector.navigatorKey,
navigatorObservers: [
...FlutterRouteInspector.navigatorObservers,
],
// ...
);
For nested Navigator widgets, pass the same observer list on each navigator
you want included. To ignore secondary navigators, keep
onlyPrimaryNavigator: true (default) and use the same navigatorKey as the
MaterialApp you are debugging.
Bottom navigation & tabs #
Switching tabs often does not push a [Route] — the shell route (e.g. your
BottomNavigationBar host) stays on top, so the observer still shows that shell
name only.
Options:
-
Manual tab reporting (simplest) — when the user changes tab, call:
FlutterRouteInspector.reportTabChange( 'OrdersTab', previousTab: 'HomeTab', );The overlay shows Route: (navigator top) and Tab: (last reported tab), and
[TAB]lines appear in the timeline and exports. UseclearTabLabel()to clear the tab line without wiping history. -
Nested navigators — if each tab has its own
Navigator, add...FlutterRouteInspector.navigatorObserversto that navigator’snavigatorObserversso inner pushes are observed (may requireonlyPrimaryNavigator: falseand matching keys depending on your setup).
Overlay: hide screen durations #
showScreenTimeOnOverlay: false hides the [screen: …s] segment in the overlay
list only. Console lines and exportLogsAsText() / exportLogsAsJsonLines()
are unchanged so you can still collect timings elsewhere.
Overlay: collapsed title, bounds, hide gesture #
- Collapsed header shows the current route and tab (if set) instead of the static “Route Inspector” label so the bubble stays informative when minimized.
- The bubble cannot be dragged off-screen; it stays within the window (with safe-area margins).
- Long-press the bubble: a dimmed scrim and a bottom strip appear (similar in spirit to chat-head “remove” targets). Drag the bubble into that strip and release to hide the overlay. Tap the scrim to cancel.
- After hiding, call
FlutterRouteInspector.presentOverlay()to attach the HUD again (navigation logging continues either way). - The package keeps a single
OverlayEntryfor the HUD; it is not duplicated when the first frame and a route transition both try to attach the overlay.
Route naming #
Labels drive the overlay header, timeline, duplicate detection, and exports. The built-in observer resolves names in this order:
RouteSettings.namewhen non-empty (best for stable IDs and analytics).RouteSettings.arguments— a non-emptyString, a concreteWidgettype name, or map keys:name,routeName,debugLabel,title.- Standard page routes with a host context — when the route is a
MaterialPageRoute,CupertinoPageRoute, orPageRouteBuilder, the resolver may callbuilder/pageBuilderonce (with the navigator’sBuildContext) and use the root page widget’sruntimeType(e.g.ProfilePage), peeling common framework wrappers such asKeyedSubtree/MediaQuerywhen needed.
The observer passesroute.navigator?.context(and falls back tonavigatorKey.currentContext), so normal pushes get this behavior for free. - Sanitized
route.runtimeType(e.g. strips generic noise fromMaterialPageRoute<void>). AnonymousRoute(<hash>)as a last resort.
So this anonymous push is labeled ProfilePage in the HUD and logs (not
MaterialPageRoute):
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (_) => const ProfilePage(),
),
);
Caveats
- Resolver inspection invokes your
builder/pageBuilderto read the widget tree. Keep those builders light and side-effect free; preferRouteSettings(name: '…')for production-grade stable names and zero extra work. - If you call
RouteNameResolver.resolveyourself without a hostBuildContext, step (3) is skipped and you fall back to step (4).
Programmatic API (overview) #
Useful for custom dashboards, tests, or conditional overlay:
| API | Description |
|---|---|
FlutterRouteInspector.isInitialized |
Whether initialize has run |
FlutterRouteInspector.currentRouteLabel |
Latest resolved top route label |
FlutterRouteInspector.auxiliaryLabel |
Last reportTabChange label, if any |
FlutterRouteInspector.events |
Immutable copy of the transition list |
FlutterRouteInspector.debugStore |
Listenable store for ListenableBuilder |
FlutterRouteInspector.exportLogsAsText() |
Plain-text export |
FlutterRouteInspector.exportLogsAsJsonLines() |
JSON Lines export |
FlutterRouteInspector.clearLogs() |
Clear in-memory history |
FlutterRouteInspector.refreshOverlay() |
Re-attach or refresh HUD after navigation |
FlutterRouteInspector.presentOverlay() |
Show HUD again after user dismissed it |
FlutterRouteInspector.disposeOverlay() |
Remove HUD entry and clear overlay hook |
You can also use RouteNameResolver directly (exported) for tools that resolve
labels outside the observer:
const resolver = RouteNameResolver();
final label = resolver.resolve(route, navigatorContext);
Logging & export #
The overlay Export button copies the current plain-text log to the clipboard,
shows a teal banner on the HUD (“Copied to clipboard” or “Could not copy”) for
about 2.5 seconds, and prints a short confirmation to the log callback /
debugPrint. Clear wipes the in-memory timeline. Tap the header (title bar)
to expand or collapse; drag the header to move the bubble.
Console output includes lines such as:
[ROUTE PUSH] HomeScreen -> ProfileScreen
[SCREEN TIME] ProfileScreen: 12.4s
Programmatic export:
final text = FlutterRouteInspector.exportLogsAsText();
final jsonl = FlutterRouteInspector.exportLogsAsJsonLines();
Drive custom UI from FlutterRouteInspector.debugStore (Listenable).
Example #
The example/ app is a minimal runnable integration: it initializes the
inspector with the overlay on, wires navigatorKey and observers, and demonstrates
both named and anonymous pushes to the same ProfilePage so you can compare
labels in the HUD and console.
Run from the repo root:
cd example && flutter run
Topics #
Pub topics: navigation, debugging, logging, observer.
License #
See LICENSE.