resolve method Null safety

  1. @override
Iterable<RouteTransitionRecord> resolve(
  1. {required List<RouteTransitionRecord> newPageRouteHistory,
  2. required Map<RouteTransitionRecord?, RouteTransitionRecord> locationToExitingPageRoute,
  3. required Map<RouteTransitionRecord?, List<RouteTransitionRecord>> pageRouteToPagelessRoutes}
)
override

A method that will be called by the Navigator to decide how routes transition in or out of the screen when Navigator.pages is updated.

The newPageRouteHistory list contains all page-based routes in the order that will be on the Navigator's history stack after this update completes. If a route in newPageRouteHistory has its RouteTransitionRecord.isWaitingForEnteringDecision set to true, this route requires explicit decision on how it should transition onto the Navigator. To make a decision, call RouteTransitionRecord.markForPush or RouteTransitionRecord.markForAdd.

The locationToExitingPageRoute contains the pages-based routes that are removed from the routes history after page update. This map records page-based routes to be removed with the location of the route in the original route history before the update. The keys are the locations represented by the page-based routes that are directly below the removed routes, and the value are the page-based routes to be removed. The location is null if the route to be removed is the bottom most route. If a route in locationToExitingPageRoute has its RouteTransitionRecord.isWaitingForExitingDecision set to true, this route requires explicit decision on how it should transition off the Navigator. To make a decision for a removed route, call RouteTransitionRecord.markForPop, RouteTransitionRecord.markForComplete or RouteTransitionRecord.markForRemove. It is possible that decisions are not required for routes in the locationToExitingPageRoute. This can happen if the routes have already been popped in earlier page updates and are still waiting for popping animations to finish. In such case, those routes are still included in the locationToExitingPageRoute with their RouteTransitionRecord.isWaitingForExitingDecision set to false and no decisions are required.

The pageRouteToPagelessRoutes records the page-based routes and their associated pageless routes. If a page-based route is waiting for exiting decision, its associated pageless routes also require explicit decisions on how to transition off the screen.

Once all the decisions have been made, this method must merge the removed routes (whether or not they require decisions) and the newPageRouteHistory and return the merged result. The order in the result will be the order the Navigator uses for updating the route history. The return list must preserve the same order of routes in newPageRouteHistory. The removed routes, however, can be inserted into the return list freely as long as all of them are included.

For example, consider the following case.

newPageRouteHistory = [A, B, C]

locationToExitingPageRoute = {A -> D, C -> E}

The following outputs are valid.

result = [A, B ,C ,D ,E] is valid. result = [D, A, B ,C ,E] is also valid because exiting route can be inserted in any place.

The following outputs are invalid.

result = [B, A, C ,D ,E] is invalid because B must be after A. result = [A, B, C ,E] is invalid because results must include D.

See also:

Implementation

@override
Iterable<RouteTransitionRecord> resolve({
  required List<RouteTransitionRecord> newPageRouteHistory,
  required Map<RouteTransitionRecord?, RouteTransitionRecord> locationToExitingPageRoute,
  required Map<RouteTransitionRecord?, List<RouteTransitionRecord>> pageRouteToPagelessRoutes,
}) {
  final List<RouteTransitionRecord> results = <RouteTransitionRecord>[];
  // This method will handle the exiting route and its corresponding pageless
  // route at this location. It will also recursively check if there is any
  // other exiting routes above it and handle them accordingly.
  void handleExitingRoute(RouteTransitionRecord? location, bool isLast) {
    final RouteTransitionRecord? exitingPageRoute = locationToExitingPageRoute[location];
    if (exitingPageRoute == null) {
      return;
    }
    if (exitingPageRoute.isWaitingForExitingDecision) {
      final bool hasPagelessRoute = pageRouteToPagelessRoutes.containsKey(exitingPageRoute);
      final bool isLastExitingPageRoute = isLast && !locationToExitingPageRoute.containsKey(exitingPageRoute);
      if (isLastExitingPageRoute && !hasPagelessRoute) {
        exitingPageRoute.markForPop(exitingPageRoute.route.currentResult);
      } else {
        exitingPageRoute.markForComplete(exitingPageRoute.route.currentResult);
      }
      if (hasPagelessRoute) {
        final List<RouteTransitionRecord> pagelessRoutes = pageRouteToPagelessRoutes[exitingPageRoute]!;
        for (final RouteTransitionRecord pagelessRoute in pagelessRoutes) {
          // It is possible that a pageless route that belongs to an exiting
          // page-based route does not require exiting decision. This can
          // happen if the page list is updated right after a Navigator.pop.
          if (pagelessRoute.isWaitingForExitingDecision) {
            if (isLastExitingPageRoute && pagelessRoute == pagelessRoutes.last) {
              pagelessRoute.markForPop(pagelessRoute.route.currentResult);
            } else {
              pagelessRoute.markForComplete(pagelessRoute.route.currentResult);
            }
          }
        }
      }
    }
    results.add(exitingPageRoute);

    // It is possible there is another exiting route above this exitingPageRoute.
    handleExitingRoute(exitingPageRoute, isLast);
  }

  // Handles exiting route in the beginning of list.
  handleExitingRoute(null, newPageRouteHistory.isEmpty);

  for (final RouteTransitionRecord pageRoute in newPageRouteHistory) {
    final bool isLastIteration = newPageRouteHistory.last == pageRoute;
    if (pageRoute.isWaitingForEnteringDecision) {
      if (!locationToExitingPageRoute.containsKey(pageRoute) && isLastIteration) {
        pageRoute.markForPush();
      } else {
        pageRoute.markForAdd();
      }
    }
    results.add(pageRoute);
    handleExitingRoute(pageRoute, isLastIteration);
  }
  return results;
}