In this article, I’ll explain how to use RouteObserver and RouteAware in Flutter to automatically track screen transitions.

Why would I ever need to do that?

Best case scenario when you develop an app that gets released is that it’s an immediate success and users love it and use it exactly the way you anticipated. Reality is usually not like that, and you will need every piece of information available to understand how users use your product.

We have out of the box solutions like google analytics that gives us tons of metrics like sessions per user, screen navigations, notifications received and so on. But sometimes we might want to capture our own events and react to them within the app, or we might have a custom analytics solution and we want to handle every event ourselves.

You said RouteObserver

As usual with Flutter, there is a very handy class that we can use to accomplish what we want: RouteObserver.

The way to do it as described in the documentation is quite simple and works very well for simple applications, but it gets a bit verbose for applications with many screens.

What we will do is build a very basic app with only three screens and implement the basic solution. Then we will see how to deal with many screens while keeping the same approach and, finally, I’ll show you how to build your own observer to handle the problem in a more elegant way.

First, the basics

I built a very simple app which consists of a home page with two buttons that navigate to different screens.

3 screens navigation app

RouteObserver informs subscribers whenever a route of type R is pushed on top of their own route of type R or popped from it.

This quote above is copied from the documentation for RouteObserver. What we need to do is:

Add the observer to our material app.

Subscribe to it to get the notifications we need.

First, let’s create our own RouteObserver and add it to the MaterialApp widget.

Now to the fun part, we need to implement RouteAware in every widget that might be push or pop into the routes stack.

Wow! Our 5 lines implementation for Screen3 is now a StatefulWidget with 33 lines, and we have to do that for all Screen1 , Screen2 , and Screen3 .

Does it work?

If we open our app and navigate to screen 2 and back and to screen 3 and back we will get the following logs in the console:

didPush Screen2

didPopNext Screen1

didPush Screen3

didPopNext Screen1

That’s pretty good. We pushed a new route Screen2, then popped that one and got to Screen1, then pushed Screen3 and popped that one back to Screen1.

Can we do better?

In a real app we would probably have some more logic in the screens, so implementing RouteAware in each screen would not be a dramatic change, but still, there’s quite a lot of code duplicated. Let’s fix that.

What I did was create a widget that will be used as a base for any page what we want to track. If we handle all our routes navigation within the routes property for MaterialApp we don’t even need to deal with RouteAwareWidget at any other part of the app.

This works fine and it’s not too inconvenient, but surely there’s a way we can automatically extract our route names and centralize even more the logic. We can do that by implementing our own RouteObserver .

Recipe for your own RouteObserver

As you can see in the previous code, we need to make sure we wrap every page in a special widget and also provide the name specifically. This is prone to error, so now we will create our own RouteObserver that will encapsulate all the logic and extract the name of the page automatically, which is actually surprisingly easy to do.

We are extending RouteObserver and implementing a few of its methods and, since we have a reference to the route settings, we can get the name of the current route and do whatever we want with it.

Now everything gets back to normal in MyApp .

And if we run the same experiment as we did before we get the following output:

screenName screen2

screenName /

screenName screen3

screenName /

Extra points for tabs navigation

In case you don’t explicitly navigate to the routes using push or pushNamed but rely on a TabBar or a NavigationBar you could do a mix of both approaches and use your custom observer to handle regular navigation and implement RouteAware in the routes that you don’t control.

That’s all. Feel free to ask questions in the comments. Thanks for reading!