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', ... )
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:
When you go to the current page NewPage
, this method is called.
When you go to another page from the current page NewPage
, this method is called.
When you come back to the current page NewPage
from another page, this method is called.
When you go to previous page from the current page NewPage
, in other words, when you close the current page, this method is called.
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.
The method unsubscribe
unsubscribes the given RouteAware
object from the observer.
pageRouteObserver.unsubscribe(this);
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(); } .... }