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.