buildRoute method

  1. @override
VRoute? buildRoute(
  1. VPathRequestData vPathRequestData, {
  2. required VPathMatch parentVPathMatch,
  3. required bool parentCanPop,
})
override

buildRoute must return VRoute if it constitute (which its subroutes or not) a valid route given the input parameters VRoute should describe this valid route

vPathRequestData contains all the information about the original request coming from VRouter It should not be changed and should be given as-is to its subroutes

parentRemainingPath is the part on which to base any local path WARNING: parentRemainingPath is null if the parent did not match the path in which case only absolute path should be tested.

parentPathParameters are the path parameters of every VRouteElement above this one in the route

buildRoute basically just checks for a match in stackedRoutes and if any adds this VRouteElement to the VRoute

For more info on buildRoute, see VRouteElement.buildRoute

Implementation

@override
VRoute? buildRoute(
  VPathRequestData vPathRequestData, {
  required VPathMatch parentVPathMatch,
  required bool parentCanPop,
}) {
  // Set localPath to null since a VNesterPageBase marks a limit between localPaths
  VPathMatch newVPathMatch = (parentVPathMatch is ValidVPathMatch)
      ? ValidVPathMatch(
          remainingPath: parentVPathMatch.remainingPath,
          pathParameters: parentVPathMatch.pathParameters,
          localPath: null,
          names: parentVPathMatch.names + [if (name != null) name!],
        )
      : InvalidVPathMatch(
          localPath: null,
          names: parentVPathMatch.names + [if (name != null) name!],
        );

  // Try to find valid VRoute from nestedRoutes
  VRoute? nestedRouteVRoute;
  for (var vRouteElement in nestedRoutes) {
    nestedRouteVRoute = vRouteElement.buildRoute(
      vPathRequestData,
      parentVPathMatch: newVPathMatch,
      parentCanPop: parentCanPop,
    );
    if (nestedRouteVRoute != null) {
      break;
    }
  }

  // If no child route match, this is not a match
  if (nestedRouteVRoute == null) {
    return null;
  }

  // Else also try to match stackedRoutes
  VRoute? stackedRouteVRoute;
  for (var vRouteElement in stackedRoutes) {
    stackedRouteVRoute = vRouteElement.buildRoute(
      vPathRequestData,
      parentVPathMatch: newVPathMatch,
      parentCanPop: true,
    );
    if (stackedRouteVRoute != null) {
      break;
    }
  }

  final vRouteElementNode = VRouteElementNode(
    this,
    localPath: null,
    nestedVRouteElementNode: nestedRouteVRoute.vRouteElementNode,
    stackedVRouteElementNode: stackedRouteVRoute?.vRouteElementNode,
  );

  final pathParameters = {
    ...nestedRouteVRoute.pathParameters,
    ...stackedRouteVRoute?.pathParameters ?? {},
  };

  final names = <String>[
    ...nestedRouteVRoute.names,
    ...stackedRouteVRoute?.names ?? [],
  ].toSet().toList();

  return VRoute(
    vRouteElementNode: vRouteElementNode,
    pages: [
      pageBuilder(
        key ?? ValueKey(parentVPathMatch.localPath),
        Builder(
          builder: (context) => LocalVRouterData(
            child: NotificationListener<VWidgetGuardMessage>(
              // This listen to [VWidgetGuardNotification] which is a notification
              // that a [VWidgetGuard] sends when it is created
              // When this happens, we store the VWidgetGuard and its context
              // This will be used to call its afterUpdate and beforeLeave in particular.
              onNotification: (VWidgetGuardMessage vWidgetGuardMessage) {
                // VWidgetGuardMessageRoot(
                //   vWidgetGuardState: vWidgetGuardMessage.vWidgetGuardState,
                //   localContext: vWidgetGuardMessage.localContext,
                //   associatedVRouteElement: this,
                // ).dispatch(vPathRequestData.rootVRouterContext);

                return false;
              },
              child: widgetBuilder(
                Builder(
                  builder: (BuildContext context) {
                    return VRouterHelper(
                      pages: <Page>[if (parentCanPop) EmptyPage()] +
                          (nestedRouteVRoute!.pages.isNotEmpty
                              ? nestedRouteVRoute.pages
                              : [EmptyPage()]),
                      navigatorKey: navigatorKey,
                      observers: <NavigatorObserver>[
                        VNestedObserverReporter(
                          navigatorObserversToReportTo:
                              vPathRequestData.navigatorObserversToReportTo,
                        ),
                        heroController
                      ],
                      backButtonDispatcher: ChildBackButtonDispatcher(
                        Router.of(context).backButtonDispatcher!,
                      )..takePriority(),
                      onPopPage: (route, data) {
                        // Try to pop a Nav1 page, if successful return false
                        if (!(route.settings is Page)) {
                          route.didPop(data);
                          return true;
                        }

                        // Else use [VRouterDelegate.pop]
                        late final vPopData;
                        if (data is VPopData) {
                          vPopData = data;
                        } else {
                          vPopData = VPopData(
                            elementToPop: nestedRouteVRoute!.vRouteElementNode
                                .getVRouteElementToPop(),
                            pathParameters: pathParameters,
                            queryParameters: {},
                            hash: '', // Pop to no hash be default
                            newHistoryState: {},
                          );
                        }

                        RootVRouterData.of(context).popFromElement(
                          vPopData.elementToPop,
                          pathParameters: vPopData.pathParameters,
                          queryParameters: vPopData.newHistoryState,
                          newHistoryState: vPopData.newHistoryState,
                        );
                        return false;
                      },
                      onSystemPopPage: () async {
                        // Try to pop a Nav1 page, if successful return true
                        if (navigatorKey.currentState!.isLastRouteNav1) {
                          navigatorKey.currentState!.pop();
                          return true; // We handled it
                        }

                        // Return false because we handle it in VRouter
                        return false;
                      },
                    );
                  },
                ),
              ),
            ),
            vRouteElementNode: vRouteElementNode,
            url: vPathRequestData.url,
            previousUrl: vPathRequestData.previousUrl,
            historyState: vPathRequestData.historyState,
            pathParameters: pathParameters,
            queryParameters: vPathRequestData.queryParameters,
            context: context,
          ),
        ),
        name ?? parentVPathMatch.localPath,
        VRouterDataImpl(
          previousUrl: vPathRequestData.previousUrl,
          url: vPathRequestData.url,
          pathParameters: pathParameters,
          queryParameters: vPathRequestData.queryParameters,
          historyState: vPathRequestData.historyState,
          names: names,
        ),
      ),
      ...stackedRouteVRoute?.pages ?? [],
    ],
    pathParameters: pathParameters,
    vRouteElements: <VRouteElement>[this] +
        nestedRouteVRoute.vRouteElements +
        (stackedRouteVRoute?.vRouteElements ?? []),
    names: names,
  );
}