LCOV - code coverage report
Current view: top level - src/vroute_elements - vnester_page_base.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 103 129 79.8 %
Date: 2021-04-26 23:10:51 Functions: 0 0 -

          Line data    Source code
       1             : part of '../main.dart';
       2             : 
       3             : /// A [VRouteElement] similar to [VNester] but which allows you to specify your own page
       4             : /// thanks to [pageBuilder]
       5             : class VNesterPageBase extends VRouteElement with VoidVGuard, VoidVPopHandler {
       6             :   /// A list of [VRouteElement] which widget will be accessible in [widgetBuilder]
       7             :   final List<VRouteElement> nestedRoutes;
       8             : 
       9             :   /// The list of possible routes to stack on top of this [VRouteElement]
      10             :   final List<VRouteElement> stackedRoutes;
      11             : 
      12             :   /// A function which creates the [VRouteElement.widget] associated to this [VRouteElement]
      13             :   ///
      14             :   /// [child] will be the [VRouteElement.widget] of the matched [VRouteElement] in
      15             :   /// [nestedRoutes]
      16             :   final Widget Function(Widget child) widgetBuilder;
      17             : 
      18             :   final LocalKey? key;
      19             : 
      20             :   final Page Function(LocalKey key, Widget child) pageBuilder;
      21             : 
      22           4 :   VNesterPageBase({
      23             :     required this.nestedRoutes,
      24             :     required this.widgetBuilder,
      25             :     required this.pageBuilder,
      26             :     this.stackedRoutes = const [],
      27             :     this.key,
      28             :   }) {
      29           8 :     assert(nestedRoutes.isNotEmpty,
      30             :         'The nestedRoutes of a VNester should not be null, otherwise it can\'t nest');
      31             :   }
      32             : 
      33             :   /// A key for the navigator
      34             :   /// It is created automatically
      35           3 :   late final GlobalKey<NavigatorState> navigatorKey =
      36           9 :       GlobalKey<NavigatorState>(debugLabel: 'This is the key of VNesterBase with key $key');
      37             : 
      38             :   /// A hero controller for the navigator
      39             :   /// It is created automatically
      40             :   final HeroController heroController = HeroController();
      41             : 
      42           3 :   @override
      43             :   VRoute? buildRoute(
      44             :     VPathRequestData vPathRequestData, {
      45             :     required VPathMatch parentVPathMatch,
      46             :   }) {
      47             :     // Set localPath to null since a VNesterPageBase marks a limit between localPaths
      48           3 :     VPathMatch newVPathMatch = (parentVPathMatch is ValidVPathMatch)
      49           3 :         ? ValidVPathMatch(
      50           3 :             remainingPath: parentVPathMatch.remainingPath,
      51           3 :             pathParameters: parentVPathMatch.pathParameters,
      52             :             localPath: null,
      53             :           )
      54           2 :         : InvalidVPathMatch(localPath: null);
      55             : 
      56             :     // Try to find valid VRoute from nestedRoutes
      57             :     VRoute? nestedRouteVRoute;
      58           6 :     for (var vRouteElement in nestedRoutes) {
      59           3 :       nestedRouteVRoute = vRouteElement.buildRoute(
      60             :         vPathRequestData,
      61             :         parentVPathMatch: newVPathMatch,
      62             :       );
      63             :       if (nestedRouteVRoute != null) {
      64             :         break;
      65             :       }
      66             :     }
      67             : 
      68             :     // If no child route match, this is not a match
      69             :     if (nestedRouteVRoute == null) {
      70             :       return null;
      71             :     }
      72             : 
      73             :     // Else also try to match stackedRoutes
      74             :     VRoute? stackedRouteVRoute;
      75           5 :     for (var vRouteElement in stackedRoutes) {
      76           2 :       stackedRouteVRoute = vRouteElement.buildRoute(
      77             :         vPathRequestData,
      78             :         parentVPathMatch: newVPathMatch,
      79             :       );
      80             :       if (stackedRouteVRoute != null) {
      81             :         break;
      82             :       }
      83             :     }
      84             : 
      85           3 :     final vRouteElementNode = VRouteElementNode(
      86             :       this,
      87             :       localPath: null,
      88           3 :       nestedVRouteElementNode: nestedRouteVRoute.vRouteElementNode,
      89           2 :       stackedVRouteElementNode: stackedRouteVRoute?.vRouteElementNode,
      90             :     );
      91             : 
      92           3 :     final pathParameters = {
      93           3 :       ...nestedRouteVRoute.pathParameters,
      94           8 :       ...stackedRouteVRoute?.pathParameters ?? {},
      95             :     };
      96             : 
      97           3 :     return VRoute(
      98             :       vRouteElementNode: vRouteElementNode,
      99           3 :       pages: [
     100           6 :         pageBuilder(
     101           9 :           key ?? ValueKey(parentVPathMatch.localPath),
     102           3 :           LocalVRouterData(
     103           3 :             child: NotificationListener<VWidgetGuardMessage>(
     104             :               // This listen to [VWidgetGuardNotification] which is a notification
     105             :               // that a [VWidgetGuard] sends when it is created
     106             :               // When this happens, we store the VWidgetGuard and its context
     107             :               // This will be used to call its afterUpdate and beforeLeave in particular.
     108           1 :               onNotification: (VWidgetGuardMessage vWidgetGuardMessage) {
     109           1 :                 VWidgetGuardMessageRoot(
     110           1 :                   vWidgetGuard: vWidgetGuardMessage.vWidgetGuard,
     111           1 :                   localContext: vWidgetGuardMessage.localContext,
     112             :                   associatedVRouteElement: this,
     113           2 :                 ).dispatch(vPathRequestData.rootVRouterContext);
     114             : 
     115             :                 return true;
     116             :               },
     117           6 :               child: widgetBuilder(
     118           3 :                 Builder(
     119           3 :                   builder: (BuildContext context) {
     120           3 :                     return VRouterHelper(
     121           6 :                       pages: nestedRouteVRoute!.pages.isNotEmpty
     122           3 :                           ? nestedRouteVRoute.pages
     123           0 :                           : [
     124           0 :                               MaterialPage(child: Center(child: CircularProgressIndicator())),
     125             :                             ],
     126           3 :                       navigatorKey: navigatorKey,
     127           6 :                       observers: [heroController],
     128             :                       backButtonDispatcher:
     129           9 :                           ChildBackButtonDispatcher(Router.of(context).backButtonDispatcher!),
     130           2 :                       onPopPage: (_, __) {
     131           4 :                         RootVRouterData.of(context).popFromElement(
     132           4 :                           nestedRouteVRoute!.vRouteElementNode.getVRouteElementToPop(),
     133           4 :                           pathParameters: VRouter.of(context).pathParameters,
     134             :                         );
     135             : 
     136             :                         // We always prevent popping because we handle it in VRouter
     137             :                         return false;
     138             :                       },
     139           0 :                       onSystemPopPage: () async {
     140           0 :                         await RootVRouterData.of(context).systemPopFromElement(
     141           0 :                           nestedRouteVRoute!.vRouteElementNode.getVRouteElementToPop(),
     142           0 :                           pathParameters: VRouter.of(context).pathParameters,
     143             :                         );
     144             : 
     145             :                         // We always prevent popping because we handle it in VRouter
     146             :                         return true;
     147             :                       },
     148             :                     );
     149             :                   },
     150             :                 ),
     151             :               ),
     152             :             ),
     153             :             vRouteElementNode: vRouteElementNode,
     154           3 :             url: vPathRequestData.url,
     155           3 :             previousUrl: vPathRequestData.previousUrl,
     156           3 :             historyState: vPathRequestData.historyState,
     157             :             pathParameters: pathParameters,
     158           3 :             queryParameters: vPathRequestData.queryParameters,
     159           3 :             context: vPathRequestData.rootVRouterContext,
     160             :           ),
     161             :         ),
     162           8 :         ...stackedRouteVRoute?.pages ?? [],
     163             :       ],
     164           3 :       pathParameters: nestedRouteVRoute.pathParameters,
     165           6 :       vRouteElements: <VRouteElement>[this] +
     166           6 :           nestedRouteVRoute.vRouteElements +
     167           5 :           (stackedRouteVRoute?.vRouteElements ?? []),
     168             :     );
     169             :   }
     170             : 
     171           2 :   GetPathFromNameResult getPathFromName(
     172             :     String nameToMatch, {
     173             :     required Map<String, String> pathParameters,
     174             :     required GetNewParentPathResult parentPathResult,
     175             :     required Map<String, String> remainingPathParameters,
     176             :   }) {
     177           2 :     final childNameResults = <GetPathFromNameResult>[];
     178             : 
     179             :     // Check if any nestedRoute matches the name
     180           4 :     for (var vRouteElement in nestedRoutes) {
     181           2 :       childNameResults.add(
     182           2 :         vRouteElement.getPathFromName(
     183             :           nameToMatch,
     184             :           pathParameters: pathParameters,
     185             :           parentPathResult: parentPathResult,
     186             :           remainingPathParameters: remainingPathParameters,
     187             :         ),
     188             :       );
     189           4 :       if (childNameResults.last is ValidNameResult) {
     190           1 :         return childNameResults.last;
     191             :       }
     192             :     }
     193             : 
     194             :     // Check if any subroute matches the name
     195           3 :     for (var vRouteElement in stackedRoutes) {
     196           1 :       childNameResults.add(
     197           1 :         vRouteElement.getPathFromName(
     198             :           nameToMatch,
     199             :           pathParameters: pathParameters,
     200             :           parentPathResult: parentPathResult,
     201             :           remainingPathParameters: remainingPathParameters,
     202             :         ),
     203             :       );
     204           2 :       if (childNameResults.last is ValidNameResult) {
     205           1 :         return childNameResults.last;
     206             :       }
     207             :     }
     208             : 
     209             :     // If we don't have any valid result
     210             : 
     211             :     // If some stackedRoute returned PathParamsPopError, aggregate them
     212           2 :     final pathParamsNameErrors = PathParamsErrorsNameResult(
     213             :       name: nameToMatch,
     214           2 :       values: childNameResults.fold<List<PathParamsError>>(
     215           2 :         <PathParamsError>[],
     216           2 :         (previousValue, element) {
     217           2 :           return previousValue +
     218           4 :               ((element is PathParamsErrorsNameResult) ? element.values : []);
     219             :         },
     220             :       ),
     221             :     );
     222             : 
     223             :     // If there was any PathParamsPopError, we have some pathParamsPopErrors.values
     224             :     // and therefore should return this
     225           4 :     if (pathParamsNameErrors.values.isNotEmpty) {
     226             :       return pathParamsNameErrors;
     227             :     }
     228             : 
     229             :     // Else try to find a NullPathError
     230             :     if (childNameResults
     231           8 :             .indexWhere((childNameResult) => childNameResult is NullPathErrorNameResult) !=
     232           2 :         -1) {
     233           0 :       return NullPathErrorNameResult(name: nameToMatch);
     234             :     }
     235             : 
     236             :     // Else return a NotFoundError
     237           2 :     return NotFoundErrorNameResult(name: nameToMatch);
     238             :   }
     239             : 
     240           3 :   GetPathFromPopResult getPathFromPop(
     241             :     VRouteElement elementToPop, {
     242             :     required Map<String, String> pathParameters,
     243             :     required GetNewParentPathResult parentPathResult,
     244             :   }) {
     245             :     // If vRouteElement is this, then this is the element to pop so we return null
     246           3 :     if (elementToPop == this) {
     247           0 :       if (parentPathResult is ValidParentPathResult) {
     248           0 :         return ValidPopResult(
     249           0 :           path: parentPathResult.path,
     250             :           didPop: true,
     251           0 :           poppedVRouteElements: [this],
     252             :         );
     253             :       } else {
     254           0 :         assert(parentPathResult is PathParamsErrorNewParentPath);
     255           0 :         return PathParamsPopErrors(
     256           0 :           values: [
     257           0 :             MissingPathParamsError(
     258           0 :               pathParams: pathParameters.keys.toList(),
     259           0 :               missingPathParams: (parentPathResult as PathParamsErrorNewParentPath).pathParameters,
     260             :             ),
     261             :           ],
     262             :         );
     263             :       }
     264             :     }
     265             : 
     266           3 :     final popErrorResults = <GetPathFromPopResult>[];
     267             : 
     268             :     // Try to pop from the nestedRoutes
     269           6 :     for (var vRouteElement in nestedRoutes) {
     270           3 :       GetPathFromPopResult childPopResult = vRouteElement.getPathFromPop(
     271             :         elementToPop,
     272             :         pathParameters: pathParameters,
     273             :         parentPathResult: parentPathResult,
     274             :       );
     275           3 :       if (childPopResult is ValidPopResult) {
     276           3 :         if (childPopResult.didPop) {
     277             :           // if the nestedRoute popped, we should pop too
     278           3 :           if (parentPathResult is ValidParentPathResult) {
     279           3 :             return ValidPopResult(
     280           3 :               path: parentPathResult.path,
     281             :               didPop: true,
     282             :               poppedVRouteElements:
     283           9 :                   <VRouteElement>[this] + childPopResult.poppedVRouteElements,
     284             :             );
     285             :           } else {
     286           0 :             assert(parentPathResult is PathParamsErrorNewParentPath);
     287           0 :             popErrorResults.add(
     288           0 :               PathParamsPopErrors(
     289           0 :                 values: [
     290           0 :                   MissingPathParamsError(
     291           0 :                     pathParams: pathParameters.keys.toList(),
     292             :                     missingPathParams:
     293           0 :                         (parentPathResult as PathParamsErrorNewParentPath).pathParameters,
     294             :                   ),
     295             :                 ],
     296             :               ),
     297             :             );
     298             :           }
     299             :         } else {
     300           3 :           return ValidPopResult(
     301           3 :             path: childPopResult.path,
     302             :             didPop: false,
     303           3 :             poppedVRouteElements: childPopResult.poppedVRouteElements,
     304             :           );
     305             :         }
     306             :       } else {
     307           2 :         popErrorResults.add(childPopResult);
     308             :       }
     309             :     }
     310             : 
     311             :     // Try to pop from the subRoutes
     312           4 :     for (var vRouteElement in stackedRoutes) {
     313           2 :       GetPathFromPopResult childPopResult = vRouteElement.getPathFromPop(
     314             :         elementToPop,
     315             :         pathParameters: pathParameters,
     316             :         parentPathResult: parentPathResult,
     317             :       );
     318           2 :       if (childPopResult is ValidPopResult) {
     319           2 :         return ValidPopResult(
     320           2 :           path: childPopResult.path,
     321             :           didPop: false,
     322           2 :           poppedVRouteElements: childPopResult.poppedVRouteElements,
     323             :         );
     324             :       } else {
     325           0 :         popErrorResults.add(childPopResult);
     326             :       }
     327             :     }
     328             : 
     329             :     // If we don't have any valid result
     330             : 
     331             :     // If some stackedRoute returned PathParamsPopError, aggregate them
     332           2 :     final pathParamsPopErrors = PathParamsPopErrors(
     333           2 :       values: popErrorResults.fold<List<MissingPathParamsError>>(
     334           2 :         <MissingPathParamsError>[],
     335           2 :         (previousValue, element) {
     336           6 :           return previousValue + ((element is PathParamsPopErrors) ? element.values : []);
     337             :         },
     338             :       ),
     339             :     );
     340             : 
     341             :     // If there was any PathParamsPopError, we have some pathParamsPopErrors.values
     342             :     // and therefore should return this
     343           4 :     if (pathParamsPopErrors.values.isNotEmpty) {
     344             :       return pathParamsPopErrors;
     345             :     }
     346             : 
     347             :     // If none of the stackedRoutes popped, this did not pop, and there was no path parameters issue, return not found
     348             :     // This should never happen
     349           0 :     return ErrorNotFoundGetPathFromPopResult();
     350             :   }
     351             : }

Generated by: LCOV version 1.14