"Fossies" - the Fresh Open Source Software Archive

Member "flutter-1.22.4/packages/flutter/lib/src/widgets/router.dart" (13 Nov 2020, 54911 Bytes) of package /linux/misc/flutter-1.22.4.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Dart source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 // Copyright 2014 The Flutter Authors. All rights reserved.
    2 // Use of this source code is governed by a BSD-style license that can be
    3 // found in the LICENSE file.
    4 
    5 // @dart = 2.8
    6 
    7 import 'dart:async';
    8 import 'dart:collection';
    9 
   10 import 'package:flutter/foundation.dart';
   11 import 'package:flutter/scheduler.dart';
   12 import 'package:flutter/services.dart';
   13 
   14 import 'basic.dart';
   15 import 'binding.dart';
   16 import 'framework.dart';
   17 import 'navigator.dart';
   18 
   19 /// A piece of routing information.
   20 ///
   21 /// The route information consists of a location string of the application and
   22 /// a state object that configures the application in that location.
   23 ///
   24 /// This information flows two ways, from the [RouteInformationProvider] to the
   25 /// [Router] or from the [Router] to [RouteInformationProvider].
   26 ///
   27 /// In the former case, the [RouteInformationProvider] notifies the [Router]
   28 /// widget when a new [RouteInformation] is available. The [Router] widget takes
   29 /// these information and navigates accordingly.
   30 ///
   31 /// The latter case should only happen in a web application where the [Router]
   32 /// reports route change back to web engine.
   33 class RouteInformation {
   34   /// Creates a route information.
   35   const RouteInformation({this.location, this.state});
   36 
   37   /// The location of the application.
   38   ///
   39   /// The string is usually in the format of multiple string identifiers with
   40   /// slashes in between. ex: `/`, `/path`, `/path/to/the/app`.
   41   ///
   42   /// It is equivalent to the URL in a web application.
   43   final String location;
   44 
   45   /// The state of the application in the [location].
   46   ///
   47   /// The app can have different states even in the same location. For example
   48   /// the text inside a [TextField] or the scroll position in a [ScrollView],
   49   /// these widget states can be stored in the [state].
   50   ///
   51   /// It's only used in the web application currently. In a web application,
   52   /// this property is stored into browser history entry when the [Router]
   53   /// report this route information back to the web engine through the
   54   /// [PlatformRouteInformationProvider], so we can get the url along with state
   55   /// back when the user click the forward or backward buttons.
   56   ///
   57   /// The state must be serializable.
   58   final Object state;
   59 }
   60 
   61 /// The dispatcher for opening and closing pages of an application.
   62 ///
   63 /// This widget listens for routing information from the operating system (e.g.
   64 /// an initial route provided on app startup, a new route obtained when an
   65 /// intent is received, or a notification that the user hit the system back
   66 /// button), parses route information into data of type `T`, and then converts
   67 /// that data into [Page] objects that it passes to a [Navigator].
   68 ///
   69 /// Additionally, every single part of that previous sentence can be overridden
   70 /// and configured as desired.
   71 ///
   72 /// The [routeInformationProvider] can be overridden to change how the name of
   73 /// the route is obtained. the [RouteInformationProvider.value] when the
   74 /// [Router] is first created is used as the initial route, and subsequent
   75 /// notifications from the [RouteInformationProvider] to its listeners are
   76 /// treated as notifications that the route information has changed.
   77 ///
   78 /// The [backButtonDispatcher] can be overridden to change how back button
   79 /// notifications are received. This must be a [BackButtonDispatcher], which is
   80 /// an object where callbacks can be registered, and which can be chained
   81 /// so that back button presses are delegated to subsidiary routers. The
   82 /// callbacks are invoked to indicate that the user is trying to close the
   83 /// current route (by pressing the system back button); the [Router] ensures
   84 /// that when this callback is invoked, the message is passed to the
   85 /// [routerDelegate] and its result is provided back to the
   86 /// [backButtonDispatcher]. Some platforms don't have back buttons and on those
   87 /// platforms it is completely normal that this notification is never sent. The
   88 /// common [backButtonDispatcher] for root router is an instance of
   89 /// [RootBackButtonDispatcher], which uses a [WidgetsBindingObserver] to listen
   90 /// to the `popRoute` notifications from [SystemChannels.navigation]. A
   91 /// common alternative is [ChildBackButtonDispatcher], which must be provided
   92 /// the [BackButtonDispatcher] of its ancestor [Router] (available via
   93 /// [Router.of]).
   94 ///
   95 /// The [routeInformationParser] can be overridden to change how names obtained
   96 /// from the [routeInformationProvider] are interpreted. It must implement the
   97 /// [RouteInformationParser] interface, specialized with the same type as the
   98 /// [Router] itself. This type, `T`, represents the data type that the
   99 /// [routeInformationParser] will generate.
  100 ///
  101 /// The [routerDelegate] can be overridden to change how the output of the
  102 /// [routeInformationParser] is interpreted. It must implement the
  103 /// [RouterDelegate] interface, also specialized with `T`; it takes as input
  104 /// the data (of type `T`) from the [routeInformationParser], and is responsible
  105 /// for providing a navigating widget to insert into the widget tree. The
  106 /// [RouterDelegate] interface is also [Listenable]; notifications are taken
  107 /// to mean that the [Router] needs to rebuild.
  108 ///
  109 /// ## Concerns regarding asynchrony
  110 ///
  111 /// Some of the APIs (notably those involving [RouteInformationParser] and
  112 /// [RouterDelegate]) are asynchronous.
  113 ///
  114 /// When developing objects implementing these APIs, if the work can be done
  115 /// entirely synchronously, then consider using [SynchronousFuture] for the
  116 /// future returned from the relevant methods. This will allow the [Router] to
  117 /// proceed in a completely synchronous way, which removes a number of
  118 /// complications.
  119 ///
  120 /// Using asynchronous computation is entirely reasonable, however, and the API
  121 /// is designed to support it. For example, maybe a set of images need to be
  122 /// loaded before a route can be shown; waiting for those images to be loaded
  123 /// before [RouterDelegate.setNewRoutePath] returns is a reasonable approach to
  124 /// handle this case.
  125 ///
  126 /// If an asynchronous operation is ongoing when a new one is to be started, the
  127 /// precise behavior will depend on the exact circumstances, as follows:
  128 ///
  129 /// If the active operation is a [routeInformationParser] parsing a new route information:
  130 /// that operation's result, if it ever completes, will be discarded.
  131 ///
  132 /// If the active operation is a [routerDelegate] handling a pop request:
  133 /// the previous pop is immediately completed with "false", claiming that the
  134 /// previous pop was not handled (this may cause the application to close).
  135 ///
  136 /// If the active operation is a [routerDelegate] handling an initial route
  137 /// or a pushed route, the result depends on the new operation. If the new
  138 /// operation is a pop request, then the original operation's result, if it ever
  139 /// completes, will be discarded. If the new operation is a push request,
  140 /// however, the [routeInformationParser] will be requested to start the parsing, and
  141 /// only if that finishes before the original [routerDelegate] request
  142 /// completes will that original request's result be discarded.
  143 ///
  144 /// If the identity of the [Router] widget's delegates change while an
  145 /// asynchronous operation is in progress, to keep matters simple, all active
  146 /// asynchronous operations will have their results discarded. It is generally
  147 /// considered unusual for these delegates to change during the lifetime of the
  148 /// [Router].
  149 ///
  150 /// If the [Router] itself is disposed while an an asynchronous operation is in
  151 /// progress, all active asynchronous operations will have their results
  152 /// discarded also.
  153 ///
  154 /// No explicit signals are provided to the [routeInformationParser] or
  155 /// [routerDelegate] to indicate when any of the above happens, so it is
  156 /// strongly recommended that [RouteInformationParser] and [RouterDelegate]
  157 /// implementations not perform extensive computation.
  158 ///
  159 /// ## Application architectural design
  160 ///
  161 /// An application can have zero, one, or many [Router] widgets, depending on
  162 /// its needs.
  163 ///
  164 /// An application might have no [Router] widgets if it has only one "screen",
  165 /// or if the facilities provided by [Navigator] are sufficient.
  166 ///
  167 /// A particularly elaborate application might have multiple [Router] widgets,
  168 /// in a tree configuration, with the first handling the entire route parsing
  169 /// and making the result available for routers in the subtree. The routers in
  170 /// the subtree do not participate in route information parsing but merely take the
  171 /// result from the first router to build their sub routes.
  172 ///
  173 /// Most applications only need a single [Router].
  174 ///
  175 /// ## URL updates for web applications
  176 ///
  177 /// In the web platform, it is important to keeps the URL up to date with the
  178 /// app state. This ensures the browser constructs its history entry
  179 /// correctly so that its forward and backward buttons continue to work.
  180 ///
  181 /// If the [routeInformationProvider] is a [PlatformRouteInformationProvider]
  182 /// and a app state change leads to [Router] rebuilds, the [Router] will detect
  183 /// such a event and retrieve the new route information from the
  184 /// [RouterDelegate.currentConfiguration] and the
  185 /// [RouteInformationParser.restoreRouteInformation]. If the location in the
  186 /// new route information is different from the current location, the router
  187 /// sends the new route information to the engine through the
  188 /// [PlatformRouteInformationProvider.routerReportsNewRouteInformation].
  189 ///
  190 /// By Providing implementations of these two methods in the subclasses and using
  191 /// the [PlatformRouteInformationProvider], you can enable the [Router] widget to
  192 /// update the URL in the browser automatically.
  193 ///
  194 /// You can force the [Router] to report the new route information back to the
  195 /// engine even if the [RouteInformation.location] has not changed. By calling
  196 /// the [Router.navigate], the [Router] will be forced to report the route
  197 /// information back to the engine after running the callback. This is useful
  198 /// when you want to support the browser backward and forward buttons without
  199 /// changing the URL. For example, the scroll position of a scroll view may be
  200 /// saved in the [RouteInformation.state]. If you use the [Router.navigate] to
  201 /// update the scroll position, the browser will create a new history entry with
  202 /// the [RouteInformation.state] that stores the new scroll position. when the
  203 /// users click the backward button, the browser will go back to previous scroll
  204 /// position without changing the url bar.
  205 ///
  206 /// You can also force the [Router] to ignore a one time route information
  207 /// update by providing a one time app state update in a callback and pass it
  208 /// into the [Router.neglect]. The [Router] will not report any route
  209 /// information even if it detects location change as a result of running the
  210 /// callback. This is particularly useful when you don't want the browser to
  211 /// create a browser history entry for this app state update.
  212 ///
  213 /// You can also choose to opt out of URL updates entirely. Simply ignore the
  214 /// [RouterDelegate.currentConfiguration] and the
  215 /// [RouteInformationParser.restoreRouteInformation] without providing the
  216 /// implementations will prevent the [Router] from reporting the URL back to the
  217 /// web engine. This is not recommended in general, but You may decide to opt
  218 /// out in these cases:
  219 ///
  220 /// * If you are not writing a web application.
  221 ///
  222 /// * If you have multiple router widgets in your app, then only one router
  223 ///   widget should update the URL (Usually the top-most one created by the
  224 ///   [WidgetsApp.router]/[MaterialApp.router]/[CupertinoApp.router]).
  225 ///
  226 /// * If your app does not care about the in-app navigation using the browser's
  227 ///   forward and backward buttons.
  228 ///
  229 /// Otherwise, we strongly recommend implementing the
  230 /// [RouterDelegate.currentConfiguration] and the
  231 /// [RouteInformationParser.restoreRouteInformation] to provide optimal
  232 /// user experience in the web application.
  233 class Router<T> extends StatefulWidget {
  234   /// Creates a router.
  235   ///
  236   /// The [routeInformationProvider] and [routeInformationParser] can be null if this
  237   /// router does not depend on route information. A common example is a sub router
  238   /// that builds its content completely relies on the app state.
  239   ///
  240   /// If the [routeInformationProvider] is not null, the [routeInformationParser] must
  241   /// also not be null.
  242   ///
  243   /// The [routerDelegate] must not be null.
  244   const Router({
  245     Key key,
  246     this.routeInformationProvider,
  247     this.routeInformationParser,
  248     @required this.routerDelegate,
  249     this.backButtonDispatcher,
  250   })  : assert(routeInformationProvider == null || routeInformationParser != null),
  251         assert(routerDelegate != null),
  252         super(key: key);
  253 
  254   /// The route information provider for the router.
  255   ///
  256   /// The value at the time of first build will be used as the initial route.
  257   /// The [Router] listens to this provider and rebuilds with new names when
  258   /// it notifies.
  259   ///
  260   /// This can be null if this router does not rely on the route information
  261   /// to build its content. In such case, the [routeInformationParser] can also be
  262   /// null.
  263   final RouteInformationProvider routeInformationProvider;
  264 
  265   /// The route information parser for the router.
  266   ///
  267   /// When the [Router] gets a new route information from the [routeInformationProvider],
  268   /// the [Router] uses this delegate to parse the route information and produce a
  269   /// configuration. The configuration will be used by [routerDelegate] and
  270   /// eventually rebuilds the [Router] widget.
  271   ///
  272   /// Since this delegate is the primary consumer of the [routeInformationProvider],
  273   /// it must not be null if [routeInformationProvider] is not null.
  274   final RouteInformationParser<T> routeInformationParser;
  275 
  276   /// The router delegate for the router.
  277   ///
  278   /// This delegate consumes the configuration from [routeInformationParser] and
  279   /// builds a navigating widget for the [Router].
  280   ///
  281   /// It is also the primary respondent for the [backButtonDispatcher]. The
  282   /// [Router] relies on the [RouterDelegate.popRoute] to handles the back
  283   /// button intends.
  284   ///
  285   /// If the [RouterDelegate.currentConfiguration] returns a non-null object,
  286   /// this [Router] will opt for URL updates.
  287   final RouterDelegate<T> routerDelegate;
  288 
  289   /// The back button dispatcher for the router.
  290   ///
  291   /// The two common alternatives are the [RootBackButtonDispatcher] for root
  292   /// router, or the [ChildBackButtonDispatcher] for other routers.
  293   final BackButtonDispatcher backButtonDispatcher;
  294 
  295   /// Retrieves the immediate [Router] ancestor from the given context.
  296   ///
  297   /// Use this method when you need to access the delegates in the [Router].
  298   /// For example, you need to access the [backButtonDispatcher] of the parent
  299   /// router to create a [ChildBackButtonDispatcher] for a nested router.
  300   /// Another use case may be updating the value in [routeInformationProvider]
  301   /// to navigate to a new route.
  302   static Router<dynamic> of(BuildContext context) {
  303     final _RouterScope scope = context.dependOnInheritedWidgetOfExactType<_RouterScope>();
  304     assert(scope != null);
  305     return scope.routerState.widget;
  306   }
  307 
  308   /// Forces the [Router] to run the [callback] and reports the route
  309   /// information back to the engine.
  310   ///
  311   /// The web application relies on the [Router] to report new route information
  312   /// in order to create browser history entry. The [Router] will only report
  313   /// them if it detects the [RouteInformation.location] changes. Use this
  314   /// method if you want the [Router] to report the route information even if
  315   /// the location does not change. This can be useful when you want to
  316   /// support the browser backward and forward button without changing the URL.
  317   ///
  318   /// For example, you can store certain state such as the scroll position into
  319   /// the [RouteInformation.state]. If you use this method to update the
  320   /// scroll position multiple times with the same URL, the browser will create
  321   /// a stack of new history entries with the same URL but different
  322   /// [RouteInformation.state]s that store the new scroll positions. If the user
  323   /// click the backward button in the browser, the browser will restore the
  324   /// scroll positions saved in history entries without changing the URL.
  325   ///
  326   /// See also:
  327   ///
  328   ///  * [Router]: see the "URL updates for web applications" section for more
  329   ///    information about route information reporting.
  330   ///  * [neglect]: which forces the [Router] to not report the route
  331   ///    information even if location does change.
  332   static void navigate(BuildContext context, VoidCallback callback) {
  333     final _RouterScope scope = context
  334       .getElementForInheritedWidgetOfExactType<_RouterScope>()
  335       .widget as _RouterScope;
  336     scope.routerState._setStateWithExplicitReportStatus(_IntentionToReportRouteInformation.must, callback);
  337   }
  338 
  339   /// Forces the [Router] to to run the [callback] without reporting the route
  340   /// information back to the engine.
  341   ///
  342   /// Use this method if you don't want the [Router] to report the new route
  343   /// information even if it detects changes as a result of running the
  344   /// [callback].
  345   ///
  346   /// The web application relies on the [Router] to report new route information
  347   /// in order to create browser history entry. The [Router] will report them
  348   /// automatically if it detects the [RouteInformation.location] changes. You
  349   /// can use this method if you want to navigate to a new route without
  350   /// creating the browser history entry.
  351   ///
  352   /// See also:
  353   ///
  354   ///  * [Router]: see the "URL updates for web applications" section for more
  355   ///    information about route information reporting.
  356   ///  * [navigate]: which forces the [Router] to report the route information
  357   ///    even if location does not change.
  358   static void neglect(BuildContext context, VoidCallback callback) {
  359     final _RouterScope scope = context
  360       .getElementForInheritedWidgetOfExactType<_RouterScope>()
  361       .widget as _RouterScope;
  362     scope.routerState._setStateWithExplicitReportStatus(_IntentionToReportRouteInformation.ignore, callback);
  363   }
  364 
  365   @override
  366   State<Router<T>> createState() => _RouterState<T>();
  367 }
  368 
  369 typedef _AsyncPassthrough<Q> = Future<Q> Function(Q);
  370 
  371 // Whether to report the route information in this build cycle.
  372 enum _IntentionToReportRouteInformation {
  373   // We haven't receive any signal on whether to report.
  374   none,
  375   // Report if route information changes.
  376   maybe,
  377   // Report regardless of route information changes.
  378   must,
  379   // Don't report regardless of route information changes.
  380   ignore,
  381 }
  382 
  383 class _RouterState<T> extends State<Router<T>> {
  384   Object _currentRouteInformationParserTransaction;
  385   Object _currentRouterDelegateTransaction;
  386   _IntentionToReportRouteInformation _currentIntentionToReport;
  387 
  388   @override
  389   void initState() {
  390     super.initState();
  391     widget.routeInformationProvider?.addListener(_handleRouteInformationProviderNotification);
  392     widget.backButtonDispatcher?.addCallback(_handleBackButtonDispatcherNotification);
  393     widget.routerDelegate.addListener(_handleRouterDelegateNotification);
  394     _currentIntentionToReport = _IntentionToReportRouteInformation.none;
  395     if (widget.routeInformationProvider != null) {
  396       _processInitialRoute();
  397     }
  398   }
  399 
  400   bool _routeInformationReportingTaskScheduled = false;
  401 
  402   String _lastSeenLocation;
  403 
  404   void _scheduleRouteInformationReportingTask() {
  405     if (_routeInformationReportingTaskScheduled)
  406       return;
  407     assert(_currentIntentionToReport != _IntentionToReportRouteInformation.none);
  408     _routeInformationReportingTaskScheduled = true;
  409     SchedulerBinding.instance.addPostFrameCallback(_reportRouteInformation);
  410   }
  411 
  412   void _reportRouteInformation(Duration timestamp) {
  413     assert(_routeInformationReportingTaskScheduled);
  414     _routeInformationReportingTaskScheduled = false;
  415 
  416     switch (_currentIntentionToReport) {
  417       case _IntentionToReportRouteInformation.none:
  418         assert(false);
  419         return;
  420 
  421       case _IntentionToReportRouteInformation.ignore:
  422         // In the ignore case, we still want to update the _lastSeenLocation.
  423         final RouteInformation routeInformation = _retrieveNewRouteInformation();
  424         if (routeInformation != null) {
  425           _lastSeenLocation = routeInformation.location;
  426         }
  427         _currentIntentionToReport = _IntentionToReportRouteInformation.none;
  428         return;
  429 
  430       case _IntentionToReportRouteInformation.maybe:
  431         final RouteInformation routeInformation = _retrieveNewRouteInformation();
  432         if (routeInformation != null) {
  433           if (_lastSeenLocation != routeInformation.location) {
  434             widget.routeInformationProvider.routerReportsNewRouteInformation(routeInformation);
  435             _lastSeenLocation = routeInformation.location;
  436           }
  437         }
  438         _currentIntentionToReport = _IntentionToReportRouteInformation.none;
  439         return;
  440 
  441       case _IntentionToReportRouteInformation.must:
  442         final RouteInformation routeInformation = _retrieveNewRouteInformation();
  443         if (routeInformation != null) {
  444           widget.routeInformationProvider.routerReportsNewRouteInformation(routeInformation);
  445           _lastSeenLocation = routeInformation.location;
  446         }
  447         _currentIntentionToReport = _IntentionToReportRouteInformation.none;
  448         return;
  449     }
  450   }
  451 
  452   RouteInformation _retrieveNewRouteInformation() {
  453     final T configuration = widget.routerDelegate.currentConfiguration;
  454     if (configuration == null)
  455       return null;
  456     final RouteInformation routeInformation = widget.routeInformationParser.restoreRouteInformation(configuration);
  457     assert((){
  458       if (routeInformation == null) {
  459         FlutterError.reportError(
  460           const FlutterErrorDetails(
  461             exception:
  462               'Router.routeInformationParser returns a null RouteInformation. '
  463               'If you opt for route information reporting, the '
  464               'routeInformationParser must not report null for a given '
  465               'configuration.'
  466           ),
  467         );
  468       }
  469       return true;
  470     }());
  471     return routeInformation;
  472   }
  473 
  474   void _setStateWithExplicitReportStatus(
  475     _IntentionToReportRouteInformation status,
  476     VoidCallback fn,
  477   ) {
  478     assert(status != null);
  479     assert(status.index >= _IntentionToReportRouteInformation.must.index);
  480     assert(() {
  481       if (_currentIntentionToReport.index >= _IntentionToReportRouteInformation.must.index &&
  482           _currentIntentionToReport != status) {
  483         FlutterError.reportError(
  484           const FlutterErrorDetails(
  485             exception:
  486               'Both Router.navigate and Router.neglect have been called in this '
  487               'build cycle, and the Router cannot decide whether to report the '
  488               'route information. Please make sure only one of them is called '
  489               'within the same build cycle.'
  490           ),
  491         );
  492       }
  493       return true;
  494     }());
  495     _currentIntentionToReport = status;
  496     _scheduleRouteInformationReportingTask();
  497     fn();
  498   }
  499 
  500   void _maybeNeedToReportRouteInformation() {
  501     _currentIntentionToReport = _currentIntentionToReport != _IntentionToReportRouteInformation.none
  502       ? _currentIntentionToReport
  503       : _IntentionToReportRouteInformation.maybe;
  504     _scheduleRouteInformationReportingTask();
  505   }
  506 
  507   @override
  508   void didChangeDependencies() {
  509     super.didChangeDependencies();
  510     _maybeNeedToReportRouteInformation();
  511   }
  512 
  513   @override
  514   void didUpdateWidget(Router<T> oldWidget) {
  515     super.didUpdateWidget(oldWidget);
  516     if (widget.routeInformationProvider != oldWidget.routeInformationProvider ||
  517         widget.backButtonDispatcher != oldWidget.backButtonDispatcher ||
  518         widget.routeInformationParser != oldWidget.routeInformationParser ||
  519         widget.routerDelegate != oldWidget.routerDelegate) {
  520       _currentRouteInformationParserTransaction = Object();
  521       _currentRouterDelegateTransaction = Object();
  522     }
  523     if (widget.routeInformationProvider != oldWidget.routeInformationProvider) {
  524       oldWidget.routeInformationProvider?.removeListener(_handleRouteInformationProviderNotification);
  525       widget.routeInformationProvider?.addListener(_handleRouteInformationProviderNotification);
  526       if (oldWidget.routeInformationProvider?.value != widget.routeInformationProvider?.value) {
  527         _handleRouteInformationProviderNotification();
  528       }
  529     }
  530     if (widget.backButtonDispatcher != oldWidget.backButtonDispatcher) {
  531       oldWidget.backButtonDispatcher?.removeCallback(_handleBackButtonDispatcherNotification);
  532       widget.backButtonDispatcher?.addCallback(_handleBackButtonDispatcherNotification);
  533     }
  534     if (widget.routerDelegate != oldWidget.routerDelegate) {
  535       oldWidget.routerDelegate.removeListener(_handleRouterDelegateNotification);
  536       widget.routerDelegate.addListener(_handleRouterDelegateNotification);
  537       _maybeNeedToReportRouteInformation();
  538     }
  539   }
  540 
  541   @override
  542   void dispose() {
  543     widget.routeInformationProvider?.removeListener(_handleRouteInformationProviderNotification);
  544     widget.backButtonDispatcher?.removeCallback(_handleBackButtonDispatcherNotification);
  545     widget.routerDelegate.removeListener(_handleRouterDelegateNotification);
  546     _currentRouteInformationParserTransaction = null;
  547     _currentRouterDelegateTransaction = null;
  548     super.dispose();
  549   }
  550 
  551   void _processInitialRoute() {
  552     _currentRouteInformationParserTransaction = Object();
  553     _currentRouterDelegateTransaction = Object();
  554     _lastSeenLocation = widget.routeInformationProvider.value.location;
  555     widget.routeInformationParser
  556       .parseRouteInformation(widget.routeInformationProvider.value)
  557       .then<T>(_verifyRouteInformationParserStillCurrent(_currentRouteInformationParserTransaction, widget))
  558       .then<void>(widget.routerDelegate.setInitialRoutePath)
  559       .then<void>(_verifyRouterDelegatePushStillCurrent(_currentRouterDelegateTransaction, widget))
  560       .then<void>(_rebuild);
  561   }
  562 
  563   void _handleRouteInformationProviderNotification() {
  564     _currentRouteInformationParserTransaction = Object();
  565     _currentRouterDelegateTransaction = Object();
  566     _lastSeenLocation = widget.routeInformationProvider.value.location;
  567     widget.routeInformationParser
  568       .parseRouteInformation(widget.routeInformationProvider.value)
  569       .then<T>(_verifyRouteInformationParserStillCurrent(_currentRouteInformationParserTransaction, widget))
  570       .then<void>(widget.routerDelegate.setNewRoutePath)
  571       .then<void>(_verifyRouterDelegatePushStillCurrent(_currentRouterDelegateTransaction, widget))
  572       .then<void>(_rebuild);
  573   }
  574 
  575   Future<bool> _handleBackButtonDispatcherNotification() {
  576     _currentRouteInformationParserTransaction = Object();
  577     _currentRouterDelegateTransaction = Object();
  578     return widget.routerDelegate
  579       .popRoute()
  580       .then<bool>(_verifyRouterDelegatePopStillCurrent(_currentRouterDelegateTransaction, widget))
  581       .then<bool>((bool data) {
  582         _rebuild();
  583         _maybeNeedToReportRouteInformation();
  584         return SynchronousFuture<bool>(data);
  585       });
  586   }
  587 
  588   static final Future<dynamic> _never = Completer<dynamic>().future; // won't ever complete
  589 
  590   _AsyncPassthrough<T> _verifyRouteInformationParserStillCurrent(Object transaction, Router<T> originalWidget) {
  591     return (T data) {
  592       if (transaction == _currentRouteInformationParserTransaction &&
  593           widget.routeInformationProvider == originalWidget.routeInformationProvider &&
  594           widget.backButtonDispatcher == originalWidget.backButtonDispatcher &&
  595           widget.routeInformationParser == originalWidget.routeInformationParser &&
  596           widget.routerDelegate == originalWidget.routerDelegate) {
  597         return SynchronousFuture<T>(data);
  598       }
  599       return _never as Future<T>;
  600     };
  601   }
  602 
  603   _AsyncPassthrough<void> _verifyRouterDelegatePushStillCurrent(Object transaction, Router<T> originalWidget) {
  604     return (void data) {
  605       if (transaction == _currentRouterDelegateTransaction &&
  606           widget.routeInformationProvider == originalWidget.routeInformationProvider &&
  607           widget.backButtonDispatcher == originalWidget.backButtonDispatcher &&
  608           widget.routeInformationParser == originalWidget.routeInformationParser &&
  609           widget.routerDelegate == originalWidget.routerDelegate)
  610         return SynchronousFuture<void>(data);
  611       return _never;
  612     };
  613   }
  614 
  615   _AsyncPassthrough<bool> _verifyRouterDelegatePopStillCurrent(Object transaction, Router<T> originalWidget) {
  616     return (bool data) {
  617       if (transaction == _currentRouterDelegateTransaction &&
  618           widget.routeInformationProvider == originalWidget.routeInformationProvider &&
  619           widget.backButtonDispatcher == originalWidget.backButtonDispatcher &&
  620           widget.routeInformationParser == originalWidget.routeInformationParser &&
  621           widget.routerDelegate == originalWidget.routerDelegate) {
  622         return SynchronousFuture<bool>(data);
  623       }
  624       // A rebuilt was trigger from a different source. Returns true to
  625       // prevent bubbling.
  626       return SynchronousFuture<bool>(true);
  627     };
  628   }
  629 
  630   Future<void> _rebuild([void value]) {
  631     setState(() {/* routerDelegate is ready to rebuild */});
  632     return SynchronousFuture<void>(value);
  633   }
  634 
  635   void _handleRouterDelegateNotification() {
  636     setState(() {/* routerDelegate wants to rebuild */});
  637     _maybeNeedToReportRouteInformation();
  638   }
  639 
  640   @override
  641   Widget build(BuildContext context) {
  642     return _RouterScope(
  643       routeInformationProvider: widget.routeInformationProvider,
  644       backButtonDispatcher: widget.backButtonDispatcher,
  645       routeInformationParser: widget.routeInformationParser,
  646       routerDelegate: widget.routerDelegate,
  647       routerState: this,
  648       child: Builder(
  649         // We use a Builder so that the build method below
  650         // will have a BuildContext that contains the _RouterScope.
  651         builder: widget.routerDelegate.build,
  652       ),
  653     );
  654   }
  655 }
  656 
  657 class _RouterScope extends InheritedWidget {
  658   const _RouterScope({
  659     Key key,
  660     @required this.routeInformationProvider,
  661     @required this.backButtonDispatcher,
  662     @required this.routeInformationParser,
  663     @required this.routerDelegate,
  664     @required this.routerState,
  665     @required Widget child,
  666   })  : assert(routeInformationProvider == null || routeInformationParser != null),
  667         assert(routerDelegate != null),
  668         assert(routerState != null),
  669         super(key: key, child: child);
  670 
  671   final ValueListenable<RouteInformation> routeInformationProvider;
  672   final BackButtonDispatcher backButtonDispatcher;
  673   final RouteInformationParser<dynamic> routeInformationParser;
  674   final RouterDelegate<dynamic> routerDelegate;
  675   final _RouterState<dynamic> routerState;
  676 
  677   @override
  678   bool updateShouldNotify(_RouterScope oldWidget) {
  679     return routeInformationProvider != oldWidget.routeInformationProvider ||
  680            backButtonDispatcher != oldWidget.backButtonDispatcher ||
  681            routeInformationParser != oldWidget.routeInformationParser ||
  682            routerDelegate != oldWidget.routerDelegate ||
  683            routerState != oldWidget.routerState;
  684   }
  685 }
  686 
  687 /// A class that can be extended or mixed in that invokes a single callback,
  688 /// which then returns a value.
  689 ///
  690 /// While multiple callbacks can be registered, when a notification is
  691 /// dispatched there must be only a single callback. The return values of
  692 /// multiple callbacks are not aggregated.
  693 ///
  694 /// `T` is the return value expected from the callback.
  695 ///
  696 /// See also:
  697 ///
  698 ///  * [Listenable] and its subclasses, which provide a similar mechanism for
  699 ///    one-way signalling.
  700 class _CallbackHookProvider<T> {
  701   final ObserverList<ValueGetter<T>> _callbacks = ObserverList<ValueGetter<T>>();
  702 
  703   /// Whether a callback is currently registered.
  704   @protected
  705   bool get hasCallbacks => _callbacks.isNotEmpty;
  706 
  707   /// Register the callback to be called when the object changes.
  708   ///
  709   /// If other callbacks have already been registered, they must be removed
  710   /// (with [removeCallback]) before the callback is next called.
  711   void addCallback(ValueGetter<T> callback) => _callbacks.add(callback);
  712 
  713   /// Remove a previously registered callback.
  714   ///
  715   /// If the given callback is not registered, the call is ignored.
  716   void removeCallback(ValueGetter<T> callback) => _callbacks.remove(callback);
  717 
  718   /// Calls the (single) registered callback and returns its result.
  719   ///
  720   /// If no callback is registered, or if the callback throws, returns
  721   /// `defaultValue`.
  722   ///
  723   /// Call this method whenever the callback is to be invoked. If there is more
  724   /// than one callback registered, this method will throw a [StateError].
  725   ///
  726   /// Exceptions thrown by callbacks will be caught and reported using
  727   /// [FlutterError.reportError].
  728   @protected
  729   T invokeCallback(T defaultValue) {
  730     if (_callbacks.isEmpty)
  731       return defaultValue;
  732     try {
  733       return _callbacks.single();
  734     } catch (exception, stack) {
  735       FlutterError.reportError(FlutterErrorDetails(
  736         exception: exception,
  737         stack: stack,
  738         library: 'widget library',
  739         context: ErrorDescription('while invoking the callback for $runtimeType'),
  740         informationCollector: () sync* {
  741           yield DiagnosticsProperty<_CallbackHookProvider<T>>(
  742             'The $runtimeType that invoked the callback was:',
  743             this,
  744             style: DiagnosticsTreeStyle.errorProperty,
  745           );
  746         },
  747       ));
  748       return defaultValue;
  749     }
  750   }
  751 }
  752 
  753 /// Report to a [Router] when the user taps the back button on platforms that
  754 /// support back buttons (such as Android).
  755 ///
  756 /// When [Router] widgets are nested, consider using a
  757 /// [ChildBackButtonDispatcher], passing it the parent [BackButtonDispatcher],
  758 /// so that the back button requests get dispatched to the appropriate [Router].
  759 /// To make this work properly, it's important that whenever a [Router] thinks
  760 /// it should get the back button messages (e.g. after the user taps inside it),
  761 /// it calls [takePriority] on its [BackButtonDispatcher] (or
  762 /// [ChildBackButtonDispatcher]) instance.
  763 ///
  764 /// The class takes a single callback, which must return a [Future<bool>]. The
  765 /// callback's semantics match [WidgetsBindingObserver.didPopRoute]'s, namely,
  766 /// the callback should return a future that completes to true if it can handle
  767 /// the pop request, and a future that completes to false otherwise.
  768 abstract class BackButtonDispatcher extends _CallbackHookProvider<Future<bool>> {
  769   LinkedHashSet<ChildBackButtonDispatcher> _children;
  770 
  771   @override
  772   bool get hasCallbacks => super.hasCallbacks || (_children != null && _children.isNotEmpty);
  773 
  774   /// Handles a pop route request.
  775   ///
  776   /// This method prioritizes the children list in reverse order and calls
  777   /// [ChildBackButtonDispatcher.notifiedByParent] on them. If any of them
  778   /// handles the request (by returning a future with true), it exits this
  779   /// method by returning this future. Otherwise, it keeps moving on to the next
  780   /// child until a child handles the request. If none of the children handles
  781   /// the request, this back button dispatcher will then try to handle the request
  782   /// by itself. This back button dispatcher handles the request by notifying the
  783   /// router which in turn calls the [RouterDelegate.popRoute] and returns its
  784   /// result.
  785   ///
  786   /// To decide whether this back button dispatcher will handle the pop route
  787   /// request, you can override the [RouterDelegate.popRoute] of the router
  788   /// delegate you pass into the router with this back button dispatcher to
  789   /// return a future of true or false.
  790   @override
  791   Future<bool> invokeCallback(Future<bool> defaultValue) {
  792     if (_children != null && _children.isNotEmpty) {
  793       final List<ChildBackButtonDispatcher> children = _children.toList();
  794       int childIndex = children.length - 1;
  795 
  796       Future<bool> notifyNextChild(bool result) {
  797         // If the previous child handles the callback, we returns the result.
  798         if (result)
  799           return SynchronousFuture<bool>(result);
  800         // If the previous child did not handle the callback, we ask the next
  801         // child to handle the it.
  802         if (childIndex > 0) {
  803           childIndex -= 1;
  804           return children[childIndex]
  805             .notifiedByParent(defaultValue)
  806             .then<bool>(notifyNextChild);
  807         }
  808         // If none of the child handles the callback, the parent will then handle it.
  809         return super.invokeCallback(defaultValue);
  810       }
  811 
  812       return children[childIndex]
  813         .notifiedByParent(defaultValue)
  814         .then<bool>(notifyNextChild);
  815     }
  816     return super.invokeCallback(defaultValue);
  817   }
  818 
  819   /// Creates a [ChildBackButtonDispatcher] that is a direct descendant of this
  820   /// back button dispatcher.
  821   ///
  822   /// To participate in handling the pop route request, call the [takePriority]
  823   /// on the [ChildBackButtonDispatcher] created from this method.
  824   ///
  825   /// When the pop route request is handled by this back button dispatcher, it
  826   /// propagate the request to its direct descendants that have called the
  827   /// [takePriority] method. If there are multiple candidates, the latest one
  828   /// that called the [takePriority] wins the right to handle the request. If
  829   /// the latest one does not handle the request (by returning a future of
  830   /// false in [ChildBackButtonDispatcher.notifiedByParent]), the second latest
  831   /// one will then have the right to handle the request. This dispatcher
  832   /// continues finding the next candidate until there are no more candidates
  833   /// and finally handles the request itself.
  834   ChildBackButtonDispatcher createChildBackButtonDispatcher() {
  835     return ChildBackButtonDispatcher(this);
  836   }
  837 
  838   /// Make this [BackButtonDispatcher] take priority among its peers.
  839   ///
  840   /// This has no effect when a [BackButtonDispatcher] has no parents and no
  841   /// children. If a [BackButtonDispatcher] does have parents or children,
  842   /// however, it causes this object to be the one to dispatch the notification
  843   /// when the parent would normally notify its callback.
  844   ///
  845   /// The [BackButtonDispatcher] must have a listener registered before it can
  846   /// be told to take priority.
  847   void takePriority() {
  848     if (_children != null)
  849       _children.clear();
  850   }
  851 
  852   /// Mark the given child as taking priority over this object and the other
  853   /// children.
  854   ///
  855   /// This causes [invokeCallback] to defer to the given child instead of
  856   /// calling this object's callback.
  857   ///
  858   /// Children are stored in a list, so that if the current child is removed
  859   /// using [forget], a previous child will return to take its place. When
  860   /// [takePriority] is called, the list is cleared.
  861   ///
  862   /// Calling this again without first calling [forget] moves the child back to
  863   /// the head of the list.
  864   ///
  865   // (Actually it moves it to the end of the list and we treat the end of the
  866   // list to be the priority end, but that's an implementation detail.)
  867   //
  868   /// The [BackButtonDispatcher] must have a listener registered before it can
  869   /// be told to defer to a child.
  870   void deferTo(ChildBackButtonDispatcher child) {
  871     assert(hasCallbacks);
  872     _children ??= <ChildBackButtonDispatcher>{} as LinkedHashSet<ChildBackButtonDispatcher>;
  873     _children.remove(child); // child may or may not be in the set already
  874     _children.add(child);
  875   }
  876 
  877   /// Causes the given child to be removed from the list of children to which
  878   /// this object might defer, as if [deferTo] had never been called for that
  879   /// child.
  880   ///
  881   /// This should only be called once per child, even if [deferTo] was called
  882   /// multiple times for that child.
  883   ///
  884   /// If no children are left in the list, this object will stop deferring to
  885   /// its children. (This is not the same as calling [takePriority], since, if
  886   /// this object itself is a [ChildBackButtonDispatcher], [takePriority] would
  887   /// additionally attempt to claim priority from its parent, whereas removing
  888   /// the last child does not.)
  889   void forget(ChildBackButtonDispatcher child) {
  890     assert(_children != null);
  891     assert(_children.contains(child));
  892     _children.remove(child);
  893   }
  894 }
  895 
  896 /// The default implementation of back button dispatcher for the root router.
  897 ///
  898 /// This dispatcher listens to platform pop route notifications. When the
  899 /// platform wants to pop the current route, this dispatcher calls the
  900 /// [BackButtonDispatcher.invokeCallback] method to handle the request.
  901 class RootBackButtonDispatcher extends BackButtonDispatcher with WidgetsBindingObserver {
  902   /// Create a root back button dispatcher.
  903   RootBackButtonDispatcher();
  904 
  905   @override
  906   void addCallback(ValueGetter<Future<bool>> callback) {
  907     if (!hasCallbacks)
  908       WidgetsBinding.instance.addObserver(this);
  909     super.addCallback(callback);
  910   }
  911 
  912   @override
  913   void removeCallback(ValueGetter<Future<bool>> callback) {
  914     super.removeCallback(callback);
  915     if (!hasCallbacks)
  916       WidgetsBinding.instance.removeObserver(this);
  917   }
  918 
  919   @override
  920   Future<bool> didPopRoute() => invokeCallback(Future<bool>.value(false));
  921 }
  922 
  923 /// A variant of [BackButtonDispatcher] which listens to notifications from a
  924 /// parent back button dispatcher, and can take priority from its parent for the
  925 /// handling of such notifications.
  926 ///
  927 /// Useful when [Router]s are being nested within each other.
  928 ///
  929 /// Use [Router.of] to obtain a reference to the nearest ancestor [Router], from
  930 /// which the [Router.backButtonDispatcher] can be found, and then used as the
  931 /// [parent] of the [ChildBackButtonDispatcher].
  932 class ChildBackButtonDispatcher extends BackButtonDispatcher {
  933   /// Creates a back button dispatcher that acts as the child of another.
  934   ///
  935   /// The [parent] must not be null.
  936   ChildBackButtonDispatcher(this.parent) : assert(parent != null);
  937 
  938   /// The back button dispatcher that this object will attempt to take priority
  939   /// over when [takePriority] is called.
  940   ///
  941   /// The parent must have a listener registered before this child object can
  942   /// have its [takePriority] or [deferTo] methods used.
  943   final BackButtonDispatcher parent;
  944 
  945   /// The parent of this child back button dispatcher decide to let this
  946   /// child to handle the invoke the  callback request in
  947   /// [BackButtonDispatcher.invokeCallback].
  948   ///
  949   /// Return a boolean future with true if this child will handle the request;
  950   /// otherwise, return a boolean future with false.
  951   @protected
  952   Future<bool> notifiedByParent(Future<bool> defaultValue) {
  953     return invokeCallback(defaultValue);
  954   }
  955 
  956   @override
  957   void takePriority() {
  958     parent.deferTo(this);
  959     super.takePriority();
  960   }
  961 
  962   @override
  963   void deferTo(ChildBackButtonDispatcher child) {
  964     assert(hasCallbacks);
  965     super.deferTo(child);
  966   }
  967 
  968   @override
  969   void removeCallback(ValueGetter<Future<bool>> callback) {
  970     super.removeCallback(callback);
  971     if (!hasCallbacks)
  972       parent.forget(this);
  973   }
  974 }
  975 
  976 /// A delegate that is used by the [Router] widget to parse a route information
  977 /// into a configuration of type T.
  978 ///
  979 /// This delegate is used when the [Router] widget is first built with initial
  980 /// route information from [Router.routeInformationProvider] and any subsequent
  981 /// new route notifications from it. The [parseRouteInformation] widget calls
  982 /// the [parseRouteInformation] with the route information.
  983 abstract class RouteInformationParser<T> {
  984   /// Abstract const constructor. This constructor enables subclasses to provide
  985   /// const constructors so that they can be used in const expressions.
  986   const RouteInformationParser();
  987 
  988   /// Converts the given route information into parsed data to pass to a
  989   /// [RouterDelegate].
  990   ///
  991   /// The method should return a future which completes when the parsing is
  992   /// complete. The parsing may be asynchronous if, e.g., the parser needs to
  993   /// communicate with the OEM thread to obtain additional data about the route.
  994   ///
  995   /// Consider using a [SynchronousFuture] if the result can be computed
  996   /// synchronously, so that the [Router] does not need to wait for the next
  997   /// microtask to pass the data to the [RouterDelegate].
  998   Future<T> parseRouteInformation(RouteInformation routeInformation);
  999 
 1000   /// Restore the route information from the given configuration.
 1001   ///
 1002   /// This is not required if you do not opt for the route information reporting
 1003   /// , which is used for updating browser history for the web application. If
 1004   /// you decides to opt in, you must also overrides this method to return a
 1005   /// route information.
 1006   ///
 1007   /// In practice, the [parseRouteInformation] method must produce an equivalent
 1008   /// configuration when passed this method's return value
 1009   RouteInformation restoreRouteInformation(T configuration) => null;
 1010 }
 1011 
 1012 /// A delegate that is used by the [Router] widget to build and configure a
 1013 /// navigating widget.
 1014 ///
 1015 /// This delegate is the core piece of the [Router] widget. It responds to
 1016 /// push route and pop route intent from the engine and notifies the [Router]
 1017 /// to rebuild. It also act as a builder for the [Router] widget and builds a
 1018 /// navigating widget, typically a [Navigator], when the [Router] widget
 1019 /// builds.
 1020 ///
 1021 /// When engine pushes a new route, the route information is parsed by the
 1022 /// [RouteInformationParser] to produce a configuration of type T. The router
 1023 /// delegate receives the configuration through [setInitialRoutePath] or
 1024 /// [setNewRoutePath] to configure itself and builds the latest navigating
 1025 /// widget upon asked.
 1026 ///
 1027 /// When implementing subclass, consider defining a listenable app state to be
 1028 /// used for building the navigating widget. The router delegate should update
 1029 /// the app state accordingly and notify the listener know the app state has
 1030 /// changed when it receive route related engine intents (e.g.
 1031 /// [setNewRoutePath], [setInitialRoutePath], or [popRoute]).
 1032 ///
 1033 /// All subclass must implement [setNewRoutePath], [popRoute], and [build].
 1034 ///
 1035 /// See also:
 1036 ///
 1037 ///  * [RouteInformationParser], which is responsible for parsing the route
 1038 ///    information to a configuration before passing in to router delegate.
 1039 ///  * [Router], which is the widget that wires all the delegates together to
 1040 ///    provide a fully functional routing solution.
 1041 abstract class RouterDelegate<T> extends Listenable {
 1042   /// Called by the [Router] at startup with the structure that the
 1043   /// [RouteInformationParser] obtained from parsing the initial route.
 1044   ///
 1045   /// This should configure the [RouterDelegate] so that when [build] is
 1046   /// invoked, it will create a widget tree that matches the initial route.
 1047   ///
 1048   /// By default, this method forwards the [configuration] to [setNewRoutePath].
 1049   ///
 1050   /// Consider using a [SynchronousFuture] if the result can be computed
 1051   /// synchronously, so that the [Router] does not need to wait for the next
 1052   /// microtask to schedule a build.
 1053   Future<void> setInitialRoutePath(T configuration) {
 1054     return setNewRoutePath(configuration);
 1055   }
 1056 
 1057   /// Called by the [Router] when the [Router.routeInformationProvider] reports that a
 1058   /// new route has been pushed to the application by the operating system.
 1059   ///
 1060   /// Consider using a [SynchronousFuture] if the result can be computed
 1061   /// synchronously, so that the [Router] does not need to wait for the next
 1062   /// microtask to schedule a build.
 1063   Future<void> setNewRoutePath(T configuration);
 1064 
 1065   /// Called by the [Router] when the [Router.backButtonDispatcher] reports that
 1066   /// the operating system is requesting that the current route be popped.
 1067   ///
 1068   /// The method should return a boolean [Future] to indicate whether this
 1069   /// delegate handles the request. Returning false will cause the entire app
 1070   /// to be popped.
 1071   ///
 1072   /// Consider using a [SynchronousFuture] if the result can be computed
 1073   /// synchronously, so that the [Router] does not need to wait for the next
 1074   /// microtask to schedule a build.
 1075   Future<bool> popRoute();
 1076 
 1077   /// Called by the [Router] when it detects a route information may have
 1078   /// changed as a result of rebuild.
 1079   ///
 1080   /// If this getter returns non-null, the [Router] will start to report new
 1081   /// route information back to the engine. In web applications, the new
 1082   /// route information is used for populating browser history in order to
 1083   /// support the forward and the backward buttons.
 1084   ///
 1085   /// When overriding this method, the configuration returned by this getter
 1086   /// must be able to construct the current app state and build the widget
 1087   /// with the same configuration in the [build] method if it is passed back
 1088   /// to the the [setNewRoutePath]. Otherwise, the browser backward and forward
 1089   /// buttons will not work properly.
 1090   ///
 1091   /// By default, this getter returns null, which prevents the [Router] from
 1092   /// reporting the route information. To opt in, a subclass can override this
 1093   /// getter to return the current configuration.
 1094   ///
 1095   /// At most one [Router] can opt in to route information reporting. Typically,
 1096   /// only the top-most [Router] created by [WidgetsApp.router] should opt for
 1097   /// route information reporting.
 1098   T get currentConfiguration => null;
 1099 
 1100   /// Called by the [Router] to obtain the widget tree that represents the
 1101   /// current state.
 1102   ///
 1103   /// This is called whenever the [setInitialRoutePath] method's future
 1104   /// completes, the [setNewRoutePath] method's future completes with the value
 1105   /// true, the [popRoute] method's future completes with the value true, or
 1106   /// this object notifies its clients (see the [Listenable] interface, which
 1107   /// this interface includes). In addition, it may be called at other times. It
 1108   /// is important, therefore, that the methods above do not update the state
 1109   /// that the [build] method uses before they complete their respective
 1110   /// futures.
 1111   ///
 1112   /// Typically this method returns a suitably-configured [Navigator]. If you do
 1113   /// plan to create a navigator, consider using the
 1114   /// [PopNavigatorRouterDelegateMixin].
 1115   ///
 1116   /// This method must not return null.
 1117   ///
 1118   /// The `context` is the [Router]'s build context.
 1119   Widget build(BuildContext context);
 1120 }
 1121 
 1122 /// A route information provider that provides route information for the
 1123 /// [Router] widget
 1124 ///
 1125 /// This provider is responsible for handing the route information through [value]
 1126 /// getter and notifies listeners, typically the [Router] widget, when a new
 1127 /// route information is available.
 1128 ///
 1129 /// When the router opts for the route information reporting (by overrides the
 1130 /// [RouterDelegate.currentConfiguration] to return non-null), overrides the
 1131 /// [routerReportsNewRouteInformation] method to process the route information.
 1132 ///
 1133 /// See also:
 1134 ///
 1135 ///  * [PlatformRouteInformationProvider], which wires up the itself with the
 1136 ///    [WidgetsBindingObserver.didPushRoute] to propagate platform push route
 1137 ///    intent to the [Router] widget, as well as reports new route information
 1138 ///    from the [Router] back to the engine by overriding the
 1139 ///    [routerReportsNewRouteInformation].
 1140 abstract class RouteInformationProvider extends ValueListenable<RouteInformation> {
 1141   /// A callback called when the [Router] widget detects any navigation event
 1142   /// due to state changes.
 1143   ///
 1144   /// The subclasses can override this method to update theirs values or trigger
 1145   /// other side effects. For example, the [PlatformRouteInformationProvider]
 1146   /// overrides this method to report the route information back to the engine.
 1147   ///
 1148   /// The [routeInformation] is the new route information after the navigation
 1149   /// event.
 1150   void routerReportsNewRouteInformation(RouteInformation routeInformation) {}
 1151 }
 1152 
 1153 /// The route information provider that propagates the platform route information changes.
 1154 ///
 1155 /// This provider also reports the new route information from the [Router] widget
 1156 /// back to engine using message channel method, the
 1157 /// [SystemNavigator.routeInformationUpdated].
 1158 class PlatformRouteInformationProvider extends RouteInformationProvider with WidgetsBindingObserver, ChangeNotifier {
 1159   /// Create a platform route information provider.
 1160   ///
 1161   /// Use the [initialRouteInformation] to set the default route information for this
 1162   /// provider.
 1163   PlatformRouteInformationProvider({
 1164     RouteInformation initialRouteInformation
 1165   }) : _value = initialRouteInformation;
 1166 
 1167   @override
 1168   void routerReportsNewRouteInformation(RouteInformation routeInformation) {
 1169     SystemNavigator.routeInformationUpdated(
 1170       location: routeInformation.location,
 1171       state: routeInformation.state,
 1172     );
 1173     _value = routeInformation;
 1174   }
 1175 
 1176   @override
 1177   RouteInformation get value => _value;
 1178   RouteInformation _value;
 1179 
 1180   void _platformReportsNewRouteInformation(RouteInformation routeInformation) {
 1181     if (_value == routeInformation)
 1182       return;
 1183     _value = routeInformation;
 1184     notifyListeners();
 1185   }
 1186 
 1187   @override
 1188   void addListener(VoidCallback listener) {
 1189     if (!hasListeners)
 1190       WidgetsBinding.instance.addObserver(this);
 1191     super.addListener(listener);
 1192   }
 1193 
 1194   @override
 1195   void removeListener(VoidCallback listener) {
 1196     super.removeListener(listener);
 1197     if (!hasListeners)
 1198       WidgetsBinding.instance.removeObserver(this);
 1199   }
 1200 
 1201   @override
 1202   void dispose() {
 1203     // In practice, this will rarely be called. We assume that the listeners
 1204     // will be added and removed in a coherent fashion such that when the object
 1205     // is no longer being used, there's no listener, and so it will get garbage
 1206     // collected.
 1207     if (hasListeners)
 1208       WidgetsBinding.instance.removeObserver(this);
 1209     super.dispose();
 1210   }
 1211 
 1212   @override
 1213   Future<bool> didPushRouteInformation(RouteInformation routeInformation) async {
 1214     assert(hasListeners);
 1215     _platformReportsNewRouteInformation(routeInformation);
 1216     return true;
 1217   }
 1218 
 1219   @override
 1220   Future<bool> didPushRoute(String route) async {
 1221     assert(hasListeners);
 1222     _platformReportsNewRouteInformation(RouteInformation(location: route));
 1223     return true;
 1224   }
 1225 }
 1226 
 1227 /// A mixin that wires [RouterDelegate.popRoute] to the [Navigator] it builds.
 1228 ///
 1229 /// This mixin calls [Navigator.maybePop] when it receives an Android back
 1230 /// button intent through the [RouterDelegate.popRoute]. Using this mixin
 1231 /// guarantees that the back button still respects pageless routes in the
 1232 /// navigator.
 1233 ///
 1234 /// Only use this mixin if you plan to build a navigator in the
 1235 /// [RouterDelegate.build].
 1236 mixin PopNavigatorRouterDelegateMixin<T> on RouterDelegate<T> {
 1237   /// The key used for retrieving the current navigator.
 1238   ///
 1239   /// When using this mixin, be sure to use this key to create the navigator.
 1240   GlobalKey<NavigatorState> get navigatorKey;
 1241 
 1242   @override
 1243   Future<bool> popRoute() {
 1244     final NavigatorState navigator = navigatorKey?.currentState;
 1245     if (navigator == null)
 1246       return SynchronousFuture<bool>(false);
 1247     return navigator.maybePop();
 1248   }
 1249 }