router_observer 1.0.0 copy "router_observer: ^1.0.0" to clipboard
router_observer: ^1.0.0 copied to clipboard

A complete navigation observability toolkit; structured events, console logging, breadcrumbs, screen tracking analytics, and an in-app debug overlay.

example/lib/main.dart

// Demonstrates every router_observer feature: console logging, breadcrumbs,
// screen tracking, nested navigators with shared listeners, popup routes,
// and the in-app debug overlay.
//
// ignore_for_file: public_member_api_docs

import 'package:flutter/material.dart';
import 'package:router_observer/router_observer.dart';

// Shared listeners: one instance covers every navigator; events carry the
// navigator label.
final breadcrumbs = NavBreadcrumbs(capacity: 50);
final consoleLogger = ConsoleNavLogger(showStack: true, showArguments: true);
final screenTracker = ScreenTracker(
  onScreenView: (view) => debugPrint(
    'SCREEN VIEW: ${view.name} on ${view.navigatorLabel}'
    '${view.timeOnPreviousScreen != null ? ' (previous screen shown for ${view.timeOnPreviousScreen!.inSeconds}s)' : ''}',
  ),
);

// One observer per navigator.
final rootObserver = RouterObserver(
  listeners: [consoleLogger, breadcrumbs, screenTracker],
  settings: const RouterObserverSettings(captureArguments: true),
);
final tabObserver = RouterObserver(
  navigatorLabel: 'tab:explore',
  listeners: [consoleLogger, breadcrumbs, screenTracker],
);

void main() => runApp(const DemoApp());

class DemoApp extends StatelessWidget {
  const DemoApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'router_observer demo',
      navigatorObservers: [rootObserver],
      builder: (context, child) => NavDebugOverlay(
        observers: [rootObserver, tabObserver],
        child: child!,
      ),
      routes: {
        '/': (_) => const HomePage(),
        '/details': (_) => const DetailsPage(),
        '/tabs': (_) => const TabsPage(),
      },
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('router_observer demo')),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          FilledButton(
            onPressed: () => Navigator.pushNamed(
              context,
              '/details',
              arguments: {'productId': 42, 'token': 'super-secret'},
            ),
            child: const Text('Push named route (with redacted args)'),
          ),
          const SizedBox(height: 8),
          FilledButton(
            onPressed: () => Navigator.push(
              context,
              MaterialPageRoute<void>(builder: (_) => const AnonymousPage()),
            ),
            child: const Text('Push anonymous route'),
          ),
          const SizedBox(height: 8),
          FilledButton(
            onPressed: () => Navigator.pushNamed(context, '/tabs'),
            child: const Text('Open nested tab navigator'),
          ),
          const SizedBox(height: 8),
          FilledButton(
            onPressed: () => showDialog<void>(
              context: context,
              builder: (_) => const AlertDialog(
                title: Text('A dialog'),
                content: Text('Logged as [dialog] by the name resolver.'),
              ),
            ),
            child: const Text('Show dialog'),
          ),
          const SizedBox(height: 8),
          FilledButton(
            onPressed: () => showModalBottomSheet<void>(
              context: context,
              builder: (_) => const SizedBox(
                height: 160,
                child: Center(child: Text('Logged as [bottom-sheet]')),
              ),
            ),
            child: const Text('Show bottom sheet'),
          ),
          const SizedBox(height: 24),
          OutlinedButton(
            onPressed: () => showDialog<void>(
              context: context,
              builder: (_) => AlertDialog(
                title: const Text('Breadcrumb trail'),
                content: SingleChildScrollView(
                  child: Text(
                    breadcrumbs.describe(),
                    style: const TextStyle(fontSize: 11),
                  ),
                ),
              ),
            ),
            child: const Text('Dump breadcrumbs (crash-report style)'),
          ),
        ],
      ),
    );
  }
}

class DetailsPage extends StatelessWidget {
  const DetailsPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Details')),
      body: Center(
        child: FilledButton(
          onPressed: () => Navigator.pushReplacementNamed(context, '/tabs'),
          child: const Text('Replace with tabs page'),
        ),
      ),
    );
  }
}

class AnonymousPage extends StatelessWidget {
  const AnonymousPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Anonymous route')),
      body: const Center(
        child: Text('This route has no name; the resolver labels it by type.'),
      ),
    );
  }
}

class TabsPage extends StatelessWidget {
  const TabsPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Nested navigator')),
      body: Navigator(
        observers: [tabObserver],
        onGenerateRoute: (settings) => MaterialPageRoute<void>(
          settings: settings.name == '/' || settings.name == null
              ? const RouteSettings(name: 'tab-root')
              : settings,
          builder: (_) => const TabRootPage(),
        ),
      ),
    );
  }
}

class TabRootPage extends StatelessWidget {
  const TabRootPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Center(
      child: FilledButton(
        onPressed: () => Navigator.of(context).push(
          MaterialPageRoute<void>(
            settings: const RouteSettings(name: 'tab-detail'),
            builder: (_) => const Scaffold(
              body: Center(child: Text('Tab detail')),
            ),
          ),
        ),
        child: const Text('Push inside nested navigator'),
      ),
    );
  }
}
0
likes
160
points
24
downloads

Documentation

Documentation
API reference

Publisher

verified publishertomars.tech

Weekly Downloads

A complete navigation observability toolkit; structured events, console logging, breadcrumbs, screen tracking analytics, and an in-app debug overlay.

Repository (GitHub)
View/report issues

Topics

#navigation #navigator #observability #telemetry #logging

License

MIT (license)

Dependencies

flutter

More

Packages that depend on router_observer