'Generic navigation listener for whole application

I'm trying to find a way to setup a global Navigation listener in the Root of my app (MaterialApp) which can allow me to track all navigation events including the actual route name (/home/page1 for ex.), navigation type etc.

I search something like:

MaterialApp(
  initialRoute: '/',
  onGenerateRoute: (RouteSettings settings) => route(settings),
  onNavigationChange: (RouteType type, String currentRoute) {
    // type can an enum be like: 'pop', 'push', 'replace' etc.
    // currentRoute is the current route name defined in a pushNamed for example
  }
),

Note: this code is non functional, only to illustrate what I want ;)

During my search, I found quite some solutions like RouteObserver, route_observer_mixin which are almost what I'm looking for but I need to wrap all my pages with a mixin... so no a global solution :/

Do you have any clues ?

Thanks in advance,

EDIT thanks to @pskink, see the solution below



Solution 1:[1]

The final solution is to create an extends of the RouteObserver like this:

class MyNavigatorObserver extends RouteObserver<PageRoute<dynamic>> {
  @override
  void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
    super.didPush(route, previousRoute);
    if (route is PageRoute) {
      // do stuff
    }
  }

  @override
  void didReplace({Route<dynamic> newRoute, Route<dynamic> oldRoute}) {
    super.didReplace(newRoute: newRoute, oldRoute: oldRoute);
    if (newRoute is PageRoute) {
      // do stuff
    }
  }

  @override
  void didPop(Route<dynamic> route, Route<dynamic> previousRoute) {
    super.didPop(route, previousRoute);
    if (previousRoute is PageRoute && route is PageRoute) {
      // do stuff
    }
  }
}

and then setup it on the MaterialApp widget:

static MyNavigatorObserver observer =
  new MyNavigatorObserver();

MaterialApp(
  initialRoute: '/',
  onGenerateRoute: (RouteSettings settings) => route(settings),
  navigatorObservers: [observer],
),

Thanks @pskink !

Solution 2:[2]

Inspired by @pskink, I've build a generic observer for logging purposes.

Designed to work with navigation 1.0 and unamed routes.

import 'package:flutter/material.dart';

class GlobalNavigatorObserver extends RouteObserver<PageRoute> {
  @override
  void didPush(route, previousRoute) {
    super.didPush(route, previousRoute);
    _log('push', previousRoute, route);
  }

  @override
  void didPop(route, previousRoute) {
    super.didPop(route, previousRoute);
    _log('pop', route, previousRoute);
  }

  void _log(String method, Route? fromRoute, Route? toRoute) {
    debugPrint('[Navigator] ${_getPageName(fromRoute)} => ${_getPageName(toRoute)} ($method)');
  }

  /// Try to get page name from route.
  String _getPageName(dynamic route) {
    // Because the builder field is not on a common class, we must try dynamically.
    try {
      final routeBuilderString = route.builder.toString();   // Example: 'Closure: (BuildContext) => WashingsPage'
      return routeBuilderString.substring(routeBuilderString.lastIndexOf(' ') + 1);
    } catch(_) {
      return 'X';
    }
  }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 iStornZ
Solution 2 Nicolas Youpi