Search code examples
flutterfirebase-analytics

Flutter Firebase analytics tracking app screen change with Navigator 2.0


All examples that I've found are using "navigatorObservers" from the MaterialApp constructor

  static FirebaseAnalytics analytics = FirebaseAnalytics.instance;
  static FirebaseAnalyticsObserver observer =
      FirebaseAnalyticsObserver(analytics: analytics);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Firebase Analytics Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      navigatorObservers: <NavigatorObserver>[observer],
      home: MyHomePage(
        title: 'Firebase Analytics Demo',
        analytics: analytics,
        observer: observer,
      ),
    );
  }

but my app uses MatterialApp.router from the Navigator 2.0 pattern and could not find an equivalent for attaching an navigatorObserver in order to track screen change events for firebase analytics. Any workarounds or suggestions on this?


Solution

  • The MaterialApp.router constructor has required routerDelegate property. This delegate is usually a wrapper of the Navigator widget. This widget has observers property - that is exactly what you are looking for.

    Here is an example of the RouterDelegate, which registers both Firebase and Segment observers:

    class AppNavigator extends RouterDelegate<void>
        with ChangeNotifier, PopNavigatorRouterDelegateMixin<void> {
      AppNavigator({
        @required Page<void> initialPage,
        this.analyticsObserver,
        this.segmentObserver
      })  : assert(initialPage != null),
            navigatorKey = GlobalKey<NavigatorState>() {
        _pagesStack = [initialPage];
      }
    
      final FirebaseAnalyticsObserver analyticsObserver;
      final SegmentObserver segmentObserver;
    
      ...
    
      @override
      Widget build(BuildContext context) {
        return Navigator(
          key: navigatorKey,
          pages: [..._pagesStack],
          observers: [analyticsObserver, segmentObserver],
          onPopPage: (route, dynamic result) {
            if (!route.didPop(result)) {
              return false;
            }
    
            for (final page in _pagesStack) {
              if (page == route.settings) {
                _pagesStack.remove(page);
                notifyListeners();
                break;
              }
            }
    
            return true;
          },
        );
      }
    }
    

    Note, that under the hood by default the Firebase Analytics module expects your page routes to have a name property set as a part of RouteSettings:

      // From FirebaseAnalyticsObserver
      void _sendScreenView(PageRoute<dynamic> route) {
        final String? screenName = nameExtractor(route.settings);
        if (screenName != null) {
          analytics.setCurrentScreen(screenName: screenName).catchError(
            (Object error) {
              final _onError = this._onError;
              if (_onError == null) {
                debugPrint('$FirebaseAnalyticsObserver: $error');
              } else {
                _onError(error as PlatformException);
              }
            },
            test: (Object error) => error is PlatformException,
          );
        }
      }
    

    You can override this behavior by providing custom nameExtractor property to the FirebaseAnalyticsObserver constructor.