RouteObserver

RouteObserver is useful when you want to do a specific task when a route is opened, or closed, when another router is opened over the current route, or when the top route is popped and the route is now visible. RouteObserver notifies its subscribers when a new route is pushed into the Navigator.

The following code creates a RouteObserver object.

final RouteObserver<PageRoute> pageRouteObserver = RouteObserver<PageRoute>();

The type on the RouteObserver class specifies the type of the route that we are going to use for. Here we are using PageRoute, which is used by Navigator when we push a widget as the page. In Flutter, a page or route is nothing but a simple widget. Consider the following statement:

Navigator.push(context, MaterialPageRoute(builder: (context) => NewPage()));

The above statement takes you to the new page NewPage. NewPage is nothing but a simple widget. The method Navigator.push accepts a Route object as the second parameter. Hence, we have wrapped our normal widget within MaterialPageRoute to create a Route object. The class MaterialPageRoute is derived from PageRoute class. The PageRoute class is indirectly derived from the base class Route. The Route class is an abstruct class and the base class of all kind of routes available in Flutter. As our pages or routes are indirectly derived from PageRoute, we can use that to work with RouteObserver.

Next, you need to specify that observer in the MaterialApp widget.

MaterialApp(
  navigatorObservers: [pageRouteObserver],
  title: 'Flutter Demo',
  ...
)

RouteAware

You have now registered the observer in your application. Next you should implement RouteAware class for those pages that are going to listen for route changes. For example, consider we have a page called NewPage, the state class of the NewPage should implement RouteAware interface to work with observer. Following code demonstrate the code implementation:

class NewPage extends StatefulWidget {
  @override
  NewPageState createState() => NewPageState();
}

// Implement RouteAware in a widget's state and subscribe it to the RouteObserver.
class NewPageState extends State<NewPage> with RouteAware {

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();

    // Subscribing to the observer --
    pageRouteObserver.subscribe(this, ModalRoute.of(context));
  }

  @override
  void dispose() {
    // Unsunscribing the observer --
    pageRouteObserver.unsubscribe(this);
    super.dispose();
  }

  @override
  void didPush() {
    // Route was pushed onto navigator and is now topmost route.
  }

  @override
  void didPopNext() {
    // Covering route was popped off the navigator.
  }

  @override
  Widget build(BuildContext context) => Container();

}

The RouteAware interface has the following four methods that you can override:

didPush()

When you go to the current page NewPage, this method is called.

didPushNext()

When you go to another page from the current page NewPage, this method is called.

didPopNext()

When you come back to the current page NewPage from another page, this method is called.

didPop()

When you go to previous page from the current page NewPage, in other words, when you close the current page, this method is called.

RouteObserver Methods

subscribe()

This method is used to subscribe a RouteAware object. It takes the RouteAware instance as the first argument, and the bounded Route object as the second argument.

pageRouteObserver.subscribe(this, ModalRoute.of(context));

As we have implemented the interface RouteAware, we can use this as the RouteAware object. That's why the first argument is this.

The statement,

ModalRoute.of(context)

retuns the closest ModalRoute object associated with the given context. The class PageRoute is a subclass of ModalRoute. As every page that uses MaterialPageRoute has the base class ModalRoute. And ModalRoute is indirectly derived from Route object, hence we can use that as the Route object as the second parameter.

unsubscribe()

The method unsubscribe unsubscribes the given RouteAware object from the observer.

pageRouteObserver.unsubscribe(this);

RouteObserver with InheritedWidget

In real life application, your pages will be in a seperate file, you need to access the observer instance to subscribe the RouteAware instance. You can use an InheritedWidget for that. Here is an example:

// InheritedWidget --
// final_app_data.dart;

import 'package:flutter/material.dart';

class FinalAppData extends InheritedWidget{

  final RouteObserver<PageRoute> pageRouteObserver;

  FinalAppData({Key key, this.pageRouteObserver, Widget child}) : super(key: key, child: child);

  static FinalAppData of(BuildContext context){
    return context.dependOnInheritedWidgetOfExactType<FinalAppData>();
  }

  @override
  bool updateShouldNotify(FinalAppData old) => false;
}

Then in your main file:

// main.dart --

final RouteObserver<PageRoute> pageRouteObserver = RouteObserver<PageRoute>();

Widget build(BuildContext context) {
  return FinalAppData(
    pageRouteObserver : pageRouteObserver,
    child: Container(),
  );

}

Now you can access the observer through the InheritedWidget:

// Demo.dart

import 'package:flutter/material.dart';
import "app_data_final.dart";


class Demo extends StatefulWidget{
  @override
  _DemoState createState()=> _DemoState();
}
class _DemoState extends State<Demo> with RouteAware{
  
  RouteObserver<PageRoute> pageRouteObserver;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    pageRouteObserver = FinalAppData.of(context).pageRouteObserver;
    pageRouteObserver.subscribe(this, ModalRoute.of(context));
  }

  @override
  dispose(){
    pageRouteObserver.unsubscribe(this);
    super.dispose();
  }

  ....

}