LCOV - code coverage report
Current view: top level - src/vroute_elements - vroute_element.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 52 62 83.9 %
Date: 2021-04-29 14:25:52 Functions: 0 0 -

          Line data    Source code
       1             : part of '../main.dart';
       2             : 
       3             : /// [VRouteElement] is the base class for any object used in routes, stackedRoutes
       4             : /// or nestedRoutes
       5             : @immutable
       6             : abstract class VRouteElement {
       7             :   /// [buildRoute] must return [VRoute] if it constitute (which its subroutes or not) a valid
       8             :   /// route given the input parameters
       9             :   /// [VRoute] should describe this valid route
      10             :   ///
      11             :   ///
      12             :   /// [vPathRequestData] contains all the information about the original request coming
      13             :   /// from [VRouter]
      14             :   /// It should not be changed and should be given as-is to its subroutes
      15             :   ///
      16             :   /// [parentRemainingPath] is the part on which to base any local path
      17             :   /// WARNING: [parentRemainingPath] is null if the parent did not match the path
      18             :   /// in which case only absolute path should be tested.
      19             :   ///
      20             :   /// [parentPathParameters] are the path parameters of every [VRouteElement] above this
      21             :   /// one in the route
      22             :   ///
      23             :   /// [buildRoute] basically just checks for a match in stackedRoutes and if any
      24             :   /// adds this [VRouteElement] to the [VRoute]
      25             :   ///
      26             :   /// For more info on buildRoute, see [VRouteElement.buildRoute]
      27             :   VRoute? buildRoute(
      28             :     VPathRequestData vPathRequestData, {
      29             :     required VPathMatch parentVPathMatch,
      30             :   });
      31             : 
      32             :   /// This function takes a name and tries to find the path corresponding to
      33             :   /// the route matching this name
      34             :   ///
      35             :   /// The deeper nested the route the better
      36             :   /// The given path parameters have to include at least every path parameters of the final path
      37             :   GetPathFromNameResult getPathFromName(
      38             :     String nameToMatch, {
      39             :     required Map<String, String> pathParameters,
      40             :     required GetNewParentPathResult parentPathResult,
      41             :     required Map<String, String> remainingPathParameters,
      42             :   });
      43             : 
      44             :   /// [GetPathFromPopResult.didPop] is true if this [VRouteElement] popped
      45             :   /// [GetPathFromPopResult.extendedPath] is null if this path can't be the right one according to
      46             :   ///                                                                     the path parameters
      47             :   /// [GetPathFromPopResult] is null when this [VRouteElement] does not pop AND none of
      48             :   ///                                                                     its stackedRoutes popped
      49             :   GetPathFromPopResult getPathFromPop(
      50             :     VRouteElement elementToPop, {
      51             :     required Map<String, String> pathParameters,
      52             :     required GetNewParentPathResult parentPathResult,
      53             :   });
      54             : 
      55             :   /// This is called before the url is updated if this [VRouteElement] was NOT in the
      56             :   /// previous route but is in the new route
      57             :   ///
      58             :   /// Use [vRedirector] if you want to redirect or stop the navigation.
      59             :   /// DO NOT use VRouter methods to redirect.
      60             :   /// [vRedirector] also has information about the route you leave and the route you go to
      61             :   ///
      62             :   /// Note that you should consider the navigation cycle to
      63             :   /// handle this precisely, see [https://vrouter.dev/guide/Advanced/Navigation%20Control/The%20Navigation%20Cycle]
      64             :   ///
      65             :   /// Also see:
      66             :   ///   * [VRouter.beforeEnter] for router level beforeEnter
      67             :   ///   * [VRedirector] to known how to redirect and have access to route information
      68             :   Future<void> beforeEnter(VRedirector vRedirector);
      69             : 
      70             :   /// This is called before the url is updated if this [VRouteElement] was in the previous
      71             :   /// route and is in the new route
      72             :   ///
      73             :   /// Use [vRedirector] if you want to redirect or stop the navigation.
      74             :   /// DO NOT use VRouter methods to redirect.
      75             :   /// [vRedirector] also has information about the route you leave and the route you go to
      76             :   ///
      77             :   /// Note that you should consider the navigation cycle to
      78             :   /// handle this precisely, see [https://vrouter.dev/guide/Advanced/Navigation%20Control/The%20Navigation%20Cycle]
      79             :   ///
      80             :   /// Also see:
      81             :   ///   * [VWidgetGuard.beforeUpdate] for widget level beforeUpdate
      82             :   ///   * [VRedirector] to known how to redirect and have access to route information
      83             :   Future<void> beforeUpdate(VRedirector vRedirector);
      84             : 
      85             :   /// Called when a url changes, before the url is updated
      86             :   /// Use [vRedirector] if you want to redirect or stop the navigation.
      87             :   /// DO NOT use VRouter methods to redirect.
      88             :   /// [vRedirector] also has information about the route you leave and the route you go to
      89             :   ///
      90             :   /// [saveHistoryState] can be used to save a history state before leaving
      91             :   /// This history state will be restored if the user uses the back button
      92             :   /// You will find the saved history state in the [VRouteElementData] using
      93             :   /// [VRouter.of(context).historyState]
      94             :   ///
      95             :   /// Note that you should consider the navigation cycle to
      96             :   /// handle this precisely, see [https://vrouter.dev/guide/Advanced/Navigation%20Control/The%20Navigation%20Cycle]
      97             :   ///
      98             :   /// Also see:
      99             :   ///   * [VRouteElement.beforeLeave] for route level beforeLeave
     100             :   ///   * [VWidgetGuard.beforeLeave] for widget level beforeLeave
     101             :   ///   * [VRedirector] to known how to redirect and have access to route information
     102             :   Future<void> beforeLeave(
     103             :     VRedirector vRedirector,
     104             :     void Function(Map<String, String> state) saveHistoryState,
     105             :   );
     106             : 
     107             :   /// This is called after the url and the historyState are updated and this [VRouteElement]
     108             :   /// was NOT in the previous route and is in the new route
     109             :   /// You can't prevent the navigation anymore
     110             :   /// You can get the new route parameters, and queryParameters
     111             :   ///
     112             :   /// Note that you should consider the navigation cycle to
     113             :   /// handle this precisely, see [https://vrouter.dev/guide/Advanced/Navigation%20Control/The%20Navigation%20Cycle]
     114             :   ///
     115             :   /// Also see:
     116             :   ///   * [VRouter.afterEnter] for router level afterEnter
     117             :   ///   * [VWidgetGuard.afterEnter] for widget level afterEnter
     118             :   void afterEnter(BuildContext context, String? from, String to);
     119             : 
     120             :   /// This is called after the url and the historyState are updated and this [VRouteElement]
     121             :   /// was in the previous route and is in the new route
     122             :   /// You can't prevent the navigation anymore
     123             :   /// You can get the new route parameters, and queryParameters
     124             :   ///
     125             :   /// Note that you should consider the navigation cycle to
     126             :   /// handle this precisely, see [https://vrouter.dev/guide/Advanced/Navigation%20Control/The%20Navigation%20Cycle]
     127             :   ///
     128             :   /// Also see:
     129             :   ///   * [VWidgetGuard.afterUpdate] for widget level afterUpdate
     130             :   void afterUpdate(BuildContext context, String? from, String to);
     131             : 
     132             :   /// Called when a pop event occurs
     133             :   /// A pop event can be called programmatically (with [VRouter.of(context).pop()])
     134             :   /// or by other widgets such as the appBar back button
     135             :   ///
     136             :   /// Use [vRedirector] if you want to redirect or stop the navigation.
     137             :   /// DO NOT use VRouter methods to redirect.
     138             :   /// [vRedirector] also has information about the route you leave and the route you go to
     139             :   ///
     140             :   /// The route you go to is calculated based on [VRouterState._defaultPop]
     141             :   ///
     142             :   /// Note that you should consider the pop cycle to
     143             :   /// handle this precisely, see [https://vrouter.dev/guide/Advanced/Pop%20Events/onPop]
     144             :   ///
     145             :   /// Also see:
     146             :   ///   * [VRouter.onPop] for router level onPop
     147             :   ///   * [VWidgetGuard.onPop] for widget level onPop
     148             :   ///   * [VRedirector] to known how to redirect and have access to route information
     149             :   Future<void> onPop(VRedirector vRedirector);
     150             : 
     151             :   /// Called when a system pop event occurs.
     152             :   /// This happens on android when the system back button is pressed.
     153             :   ///
     154             :   /// Use [vRedirector] if you want to redirect or stop the navigation.
     155             :   /// DO NOT use VRouter methods to redirect.
     156             :   /// [vRedirector] also has information about the route you leave and the route you go to
     157             :   ///
     158             :   /// The route you go to is calculated based on [VRouterState._defaultPop]
     159             :   ///
     160             :   /// Note that you should consider the systemPop cycle to
     161             :   /// handle this precisely, see [https://vrouter.dev/guide/Advanced/Pop%20Events/onSystemPop]
     162             :   ///
     163             :   /// Also see:
     164             :   ///   * [VRouter.onSystemPop] for route level onSystemPop
     165             :   ///   * [VWidgetGuard.onSystemPop] for widget level onSystemPop
     166             :   ///   * [VRedirector] to known how to redirect and have access to route information
     167             :   Future<void> onSystemPop(VRedirector vRedirector);
     168             : }
     169             : 
     170             : /// Return type of [VRouteElement.getPathFromPop]
     171             : abstract class GetPathFromPopResult {}
     172             : 
     173             : class FoundPopResult extends GetPathFromPopResult {
     174             :   /// [extendedPath] should be deducted from the parent path, [VRouteElement.path] and the path parameters,
     175             :   ///  Note the it should be null if the path can not be deduced from the said parameters
     176             :   final String? path;
     177             : 
     178             :   /// [didPop] should be true if this [VRouteElement] is to be popped
     179             :   final bool didPop;
     180             : 
     181             :   /// List of every popping [VRouteElement]
     182             :   final List<VRouteElement> poppedVRouteElements;
     183             : 
     184           9 :   FoundPopResult({
     185             :     required this.path,
     186             :     required this.didPop,
     187             :     required this.poppedVRouteElements,
     188             :   });
     189             : }
     190             : 
     191             : abstract class ErrorGetPathFromPopResult extends GetPathFromPopResult implements Exception {}
     192             : 
     193             : class ErrorNotFoundGetPathFromPopResult extends ErrorGetPathFromPopResult {
     194           0 :   @override
     195             :   String toString() =>
     196             :       'The VRouteElement to pop was not found. Please open an issue, this should never happen.';
     197             : }
     198             : 
     199             : class PathParamsPopErrors extends ErrorGetPathFromPopResult {
     200             :   final List<MissingPathParamsError> values;
     201             : 
     202           4 :   PathParamsPopErrors({
     203             :     required this.values,
     204             :   });
     205             : 
     206           1 :   @override
     207             :   String toString() =>
     208             :       'Could not pop because some path parameters where missing. \n'
     209           1 :           'Here are the possible path parameters that were expected and the missing ones:\n' +
     210           1 :       [
     211           1 :         for (var value in values)
     212           2 :           '  - Path parameters: ${value.pathParams}, missing ones: ${value.missingPathParams}'
     213           2 :       ].join('\n');
     214             : }
     215             : 
     216             : /// Return type of [VRouteElement.getPathFromName]
     217             : abstract class GetPathFromNameResult {}
     218             : 
     219             : class ValidNameResult extends GetPathFromNameResult {
     220             :   /// [extendedPath] should be deducted from the parent path, [VRouteElement.path] and the path parameters,
     221             :   ///  Note the it should be null if the path can not be deduced from the said parameters
     222             :   final String path;
     223             : 
     224           6 :   ValidNameResult({required this.path});
     225             : }
     226             : 
     227             : abstract class ErrorGetPathFromNameResult extends GetPathFromNameResult implements Error {
     228             :   String get error;
     229             : 
     230           1 :   @override
     231           1 :   String toString() => error;
     232             : 
     233           0 :   @override
     234           0 :   StackTrace? get stackTrace => StackTrace.current;
     235             : }
     236             : 
     237             : class NotFoundErrorNameResult extends ErrorGetPathFromNameResult {
     238             :   final String name;
     239             : 
     240           5 :   NotFoundErrorNameResult({required this.name});
     241             : 
     242           3 :   String get error => 'Could not find the VRouteElement named $name.';
     243             : }
     244             : 
     245             : class NullPathErrorNameResult extends ErrorGetPathFromNameResult {
     246             :   final String name;
     247             : 
     248           1 :   NullPathErrorNameResult({required this.name});
     249             : 
     250           2 :   String get error =>
     251           1 :       'The VRouteElement named $name as a null path but no parent VRouteElement with a path.\n'
     252             :       'No valid path can therefore be formed.';
     253             : }
     254             : 
     255             : abstract class PathParamsError implements Error {
     256             :   List<String> get pathParams;
     257             : 
     258             :   String get error;
     259             : 
     260           0 :   @override
     261           0 :   String toString() => error;
     262             : 
     263           0 :   @override
     264           0 :   StackTrace? get stackTrace => StackTrace.current;
     265             : }
     266             : 
     267             : class MissingPathParamsError extends PathParamsError {
     268             :   final List<String> missingPathParams;
     269             :   final List<String> pathParams;
     270             : 
     271           5 :   MissingPathParamsError({required this.pathParams, required this.missingPathParams});
     272             : 
     273           4 :   String get error => 'Path parameters given: $pathParams, missing: $missingPathParams';
     274             : }
     275             : 
     276             : class OverlyPathParamsError extends PathParamsError {
     277             :   final List<String> expectedPathParams;
     278             :   final List<String> pathParams;
     279             : 
     280           2 :   OverlyPathParamsError({required this.pathParams, required this.expectedPathParams});
     281             : 
     282           4 :   String get error => 'Path parameters given: $pathParams, expected: $expectedPathParams';
     283             : }
     284             : 
     285             : class PathParamsErrorsNameResult extends ErrorGetPathFromNameResult {
     286             :   final List<PathParamsError> values;
     287             :   final String name;
     288             : 
     289           5 :   PathParamsErrorsNameResult({required this.name, required this.values});
     290             : 
     291           1 :   @override
     292           1 :   String get error =>
     293           1 :       'Could not find value route for name $name because of path parameters. \n'
     294           1 :           'Here are the possible path parameters that were expected compared to what you gave:\n' +
     295           5 :       [for (var value in values) '  - ${value.error}'].join('\n');
     296             : }
     297             : 
     298             : /// Hold every information of the current route we are building
     299             : /// This is used is [VRouteElement.buildRoute] and should be passed down to the next
     300             : /// [VRouteElement.buildRoute] without modification.
     301             : ///
     302             : /// The is used for two purposes:
     303             : ///   1. Giving the needed information for [VRouteElement.buildRoute] to decide how/if it should
     304             : ///       build its route
     305             : ///   2. Holds information that are used to populate the [LocalVRouterData] attached to every
     306             : ///   _  [VRouteElement]
     307             : class VPathRequestData {
     308             :   /// The previous url (which is actually the current one since when [VPathRequestData] is passed
     309             :   /// the url did not yet change from [VRouter] point of view)
     310             :   final String? previousUrl;
     311             : 
     312             :   /// The new uri. This is what should be used to determine the validity of the [VRouteElement]
     313             :   final Uri uri;
     314             : 
     315             :   /// The new history state, used to populate the [LocalVRouterData]
     316             :   final Map<String, String> historyState;
     317             : 
     318             :   /// A [BuildContext] with which we can access [RootVRouterData]
     319             :   final BuildContext rootVRouterContext;
     320             : 
     321          12 :   VPathRequestData({
     322             :     required this.previousUrl,
     323             :     required this.uri,
     324             :     required this.historyState,
     325             :     required this.rootVRouterContext,
     326             :   });
     327             : 
     328             :   /// The path contained in the uri
     329          36 :   String get path => uri.path;
     330             : 
     331             :   /// The query parameters contained in the uri
     332          36 :   Map<String, String> get queryParameters => uri.queryParameters;
     333             : 
     334             :   /// The url corresponding to the uri
     335          36 :   String get url => uri.toString();
     336             : }
     337             : 
     338             : /// [VRouteElementNode] is used to represent the current route configuration as a tree
     339             : class VRouteElementNode {
     340             :   /// The [VRouteElementNode] containing the [VRouteElement] which is the current nested route
     341             :   /// to be valid, if any
     342             :   ///
     343             :   /// The is used be all types of [VNestedPage]
     344             :   final VRouteElementNode? nestedVRouteElementNode;
     345             : 
     346             :   /// The [VRouteElementNode] containing the [VRouteElement] which is the current stacked routes
     347             :   /// to be valid, if any
     348             :   final VRouteElementNode? stackedVRouteElementNode;
     349             : 
     350             :   /// The [VRouteElement] attached to this node
     351             :   final VRouteElement vRouteElement;
     352             : 
     353             :   /// The path of the [VRouteElement] attached to this node
     354             :   /// If the path has path parameters, they should be replaced
     355             :   final String? localPath;
     356             : 
     357          12 :   VRouteElementNode(
     358             :     this.vRouteElement, {
     359             :     required this.localPath,
     360             :     this.nestedVRouteElementNode,
     361             :     this.stackedVRouteElementNode,
     362             :   });
     363             : 
     364             :   /// Finding the element to pop for a [VRouteElementNode] means finding which one is at the
     365             :   /// end of the chain of stackedVRouteElementNode (if none then this should be popped)
     366           8 :   VRouteElement getVRouteElementToPop() {
     367           8 :     if (stackedVRouteElementNode != null) {
     368          12 :       return stackedVRouteElementNode!.getVRouteElementToPop();
     369             :     }
     370           8 :     return vRouteElement;
     371             :   }
     372             : 
     373             :   /// Get the [VRouteElementNode] associated to the given [VRouteElement]
     374             :   /// returns null if the [VRouteElement] is not his nor in the stackedRoutes or the subroutes
     375           7 :   VRouteElementNode? getVRouteElementNodeFromVRouteElement(VRouteElement vRouteElement) {
     376          14 :     if (vRouteElement == this.vRouteElement) return this;
     377           7 :     if (stackedVRouteElementNode != null) {
     378             :       final vRouteElementNode =
     379          14 :           stackedVRouteElementNode!.getVRouteElementNodeFromVRouteElement(vRouteElement);
     380             :       if (vRouteElementNode != null) return vRouteElementNode;
     381             :     }
     382           2 :     if (nestedVRouteElementNode != null) {
     383             :       final vRouteElementNode =
     384           4 :       nestedVRouteElementNode!.getVRouteElementNodeFromVRouteElement(vRouteElement);
     385             :       if (vRouteElementNode != null) return vRouteElementNode;
     386             :     }
     387             :     return null;
     388             :   }
     389             : 
     390             :   /// Get a flatten list of the [VRouteElement] from this + all those contained in
     391             :   /// stackedRoutes and subRoutes.
     392           7 :   List<VRouteElement> getVRouteElements() {
     393          21 :     return [vRouteElement] +
     394          21 :         (stackedVRouteElementNode?.getVRouteElements() ?? []) +
     395          14 :         (nestedVRouteElementNode?.getVRouteElements() ?? []);
     396             :   }
     397             : 
     398             :   /// This function will search this node and the nested and sub nodes to try to find the node
     399             :   /// that hosts [vRouteElement]
     400           1 :   VRouteElementNode? getChildVRouteElementNode({
     401             :     required VRouteElement vRouteElement,
     402             :   }) {
     403             :     // If this VRouteElementNode contains the given VRouteElement, return this
     404           2 :     if (vRouteElement == this.vRouteElement) {
     405             :       return this;
     406             :     }
     407             : 
     408             :     // Search if the VRouteElementNode containing the VRouteElement is in the nestedVRouteElementNode
     409           1 :     if (nestedVRouteElementNode != null) {
     410             :       VRouteElementNode? vRouteElementNode =
     411           0 :           nestedVRouteElementNode!.getChildVRouteElementNode(vRouteElement: vRouteElement);
     412             :       if (vRouteElementNode != null) {
     413             :         return vRouteElementNode;
     414             :       }
     415             :     }
     416             : 
     417             :     // Search if the VRouteElementNode containing the VRouteElement is in the stackedVRouteElementNode
     418           1 :     if (stackedVRouteElementNode != null) {
     419             :       VRouteElementNode? vRouteElementNode =
     420           2 :           stackedVRouteElementNode!.getChildVRouteElementNode(vRouteElement: vRouteElement);
     421             :       if (vRouteElementNode != null) {
     422             :         return vRouteElementNode;
     423             :       }
     424             :     }
     425             : 
     426             :     // If the VRouteElement was not find anywhere, return null
     427             :     return null;
     428             :   }
     429             : }
     430             : 
     431             : /// Part of [VRouteElement.buildRoute] that must be passed down but can be modified
     432             : abstract class VPathMatch {
     433             :   /// The local path is the one of the current VRouteElement
     434             :   /// If the path has path parameters, those should be replaced
     435             :   String? get localPath;
     436             : }
     437             : 
     438             : class ValidVPathMatch extends VPathMatch {
     439             :   /// The remaining of the path after having remove the part of the path that this
     440             :   /// [VPath] has matched
     441             :   final String remainingPath;
     442             : 
     443             :   /// The path parameters of the valid [VRoute] which are
     444             :   ///   - Empty if no valid [VRoute] has been found
     445             :   ///   - This [VPath.pathParameters] if [VRouteElement.extendedPath] is absolute
     446             :   ///   - This [VPath.pathParameters] and the parent pathParameters if  [VRouteElement.extendedPath] is relative
     447             :   final Map<String, String> pathParameters;
     448             : 
     449             :   /// The local path is the one of the current VRouteElement
     450             :   /// If the path has path parameters, those should be replaced
     451             :   final String? localPath;
     452             : 
     453          12 :   ValidVPathMatch({
     454             :     required this.remainingPath,
     455             :     required this.pathParameters,
     456             :     required this.localPath,
     457             :   });
     458             : }
     459             : 
     460             : class InvalidVPathMatch extends VPathMatch implements Error {
     461             :   /// If there is no pathMatch but a VRouteElement needs a ValueKey
     462             :   /// use a constant path (which the user is likely to pop on)
     463             :   final String? localPath;
     464             : 
     465          12 :   InvalidVPathMatch({required this.localPath});
     466             : 
     467           0 :   @override
     468           0 :   StackTrace? get stackTrace => StackTrace.current;
     469             : }

Generated by: LCOV version 1.14