The Problem#
I am developing an app (check it out here) and want to get the most basic analytics data, without much hassle. Like: "How many people are going to which screen."
                  I am using 
                  go_router, so this should be easy. We just take any Analytics tool that has a good Flutter SDK and add its 
                  NavigationObserver
                   to our router. Right? Wrong.
                
This tracks only 50% of my screens
GoRouter(
    observers: [
      FirebaseAnalyticsObserver(),
      PosthogObserver(),
      // .. etc
      ],
    routes: [
      // ...
    ],
The app runs, but a lot of pages are not tracked. I chose Posthog. But this would be the same for Firebase or any others I have used in my Flutter lifetime. (Posthog itself is great btw.)
Let's fix this! Here are 3 simple solutions that will get your analytics tracking working properly.
1. ShellRoutes need an AnalyticsObserver#
                  It makes sense if you think about it, but naturally using 
                  go_router
                   and 
                  ShellRoute
                   you don't have it on your mind.
                  Since every single 
                  ShellRoute
                   creates its own Navigator, every instance needs its own 
                  AnalyticsObserver. They are not automatically propagated to the root observer.
                  Easy fix, just sprinkle in some more observers.
                
GoRouter(
    observers: [PosthogObserver()],
    routes: [
        ShellRoute(
            // ADD THIS GUY
            observers: [PosthogObserver()]
            builder: (context, state, child) {
                // some builder
                },
            routes: [],
2. Weird RouteFilters#
                  As your app grows, you will arrive at a point where you want a more custom page, so you will use the 
                  pageBuilder.
                  Personally, I use 
                  go_router
                   to also display things like modal bottom sheets. So I have something like this:
                
    GoRoute(
      path: '/paywall',
      pageBuilder:
          (context, state) => ScrollableModalPage(
            // this name is important for fix #3
            name: 'paywall',
            builder:
                (context, scrollController) =>
                    Paywall(scrollController: scrollController),
          ),
    ),
                  This works great, but brings another surprise. Most NavigationObservers have a 
                  routeFilter
                   param and this filter often just filters out anything that is not a 
                  PageRoute. So all of my precious modal routes are filtered out and are never sent as screen events.
                
Imho, this breaks more things than it fixes. A route filter that always returns true almost would have been a better default. You can just copy the route filter that I am using, or dive deeper into that particular subject.
Use this
bool routeFilter(Route<dynamic>? route) {
  return route is PageRoute || route is Page || route is Route;
}
// and add it to your observer
GoRouter(
    observers: [
      PosthogObserver(routeFilter: routeFilter),
    ]);
3. Names are null#
In a sane world, every AnalyticsObserver would have access to the path passed routes and would just take this path as name for my fancy dashboards. But that's not the case.
                  Same as the 
                  routeFilter
                   there is a 
                  nameExtractor. It takes a RouteSettings object and returns a name. The default is just 
                  String name(RouteSettings settings) => settings.name;
                
                  That is actually fine, however, routes don't always have a name and if the name is 
                  null, nothing is tracked. Annoying.
                  This is especially frustrating, because passing a name to your GoRoute object will not fix the issue if you use a 
                  pageBuilder.
                  Here we have to pass the name further to the actual Page and furthermore we have to pass the right settings in the 
                  Route<T> createRoute(BuildContext context)
                   function.
                
So you have something like the paywall route in #2 and the following custom page:
class ScrollableModalPage<T> extends Page<T> {
  // needs a super.name
  const ScrollableModalPage({required this.builder, required super.name});
  final ScrollableWidgetBuilder builder;
  @override
  Route<T> createRoute(BuildContext context) => ModalBottomSheetRoute<T>(
    // needs to pass this as settings
    settings: this,
    // .... more stuff
Easy fix, just add names. Honestly, I started add route names everywhere, just in case. I don't think that it breaks anything.
Wrapping Up#
                  Thanks for reading! I hope these tips help you get your analytics tracking working properly.
                  Also do not think, that I don't like 
                  go_router. In fact I love it. There are just some cases, in which minor things don't work as expected, which is why I write tiny guides like this one.
                  Feel free to check out my socials for more Flutter tips and tricks.