flutter_route_inspector 1.0.6 copy "flutter_route_inspector: ^1.0.6" to clipboard
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):

Route inspector overlay

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

Collapsed floating overlay

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

Route history with duplicate route warning

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

Drag to dismiss overlay

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 Navigator runs (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 / PageRouteBuilder when 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 debugStore to 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 Navigator stack your app already uses
  • Readable route labelsRouteSettings.name, common arguments keys, 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 reportTabChange when 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 Flutter Navigator

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:

  1. 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. Use clearTabLabel() to clear the tab line without wiping history.

  2. Nested navigators — if each tab has its own Navigator, add ...FlutterRouteInspector.navigatorObservers to that navigator’s navigatorObservers so inner pushes are observed (may require onlyPrimaryNavigator: false and 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 OverlayEntry for 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:

  1. RouteSettings.name when non-empty (best for stable IDs and analytics).
  2. RouteSettings.arguments — a non-empty String, a concrete Widget type name, or map keys: name, routeName, debugLabel, title.
  3. Standard page routes with a host context — when the route is a MaterialPageRoute, CupertinoPageRoute, or PageRouteBuilder, the resolver may call builder / pageBuilder once (with the navigator’s BuildContext) and use the root page widget’s runtimeType (e.g. ProfilePage), peeling common framework wrappers such as KeyedSubtree / MediaQuery when needed.
    The observer passes route.navigator?.context (and falls back to navigatorKey.currentContext), so normal pushes get this behavior for free.
  4. Sanitized route.runtimeType (e.g. strips generic noise from MaterialPageRoute<void>).
  5. 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 / pageBuilder to read the widget tree. Keep those builders light and side-effect free; prefer RouteSettings(name: '…') for production-grade stable names and zero extra work.
  • If you call RouteNameResolver.resolve yourself without a host BuildContext, 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.

3
likes
160
points
59
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Universal navigation debugger: route timeline, screen time, duplicate-push hints, exports, and a draggable overlay. Works with Navigator 1/2, GetX, GoRouter, and AutoRoute.

Repository (GitHub)
View/report issues

Topics

#navigation #debugging #logging #observer

License

MIT (license)

Dependencies

flutter

More

Packages that depend on flutter_route_inspector