diff --git a/packages/go_router/lib/src/configuration.dart b/packages/go_router/lib/src/configuration.dart index 7f08cf133800..bd60e6c6792e 100644 --- a/packages/go_router/lib/src/configuration.dart +++ b/packages/go_router/lib/src/configuration.dart @@ -12,7 +12,7 @@ import 'package:meta/meta.dart'; import 'logging.dart'; import 'match.dart'; import 'misc/errors.dart'; -import 'route_pattern.dart'; +import 'route_path.dart'; import 'route.dart'; import 'router.dart'; import 'state.dart'; @@ -137,7 +137,7 @@ class RouteConfiguration { // Recursively search for the first GoRoute descendant. Will // throw assertion error if not found. final GoRoute? route = branch.defaultRoute; - final RoutePattern? fullPattern = route != null + final RoutePath? fullPattern = route != null ? buildRoutePatternFromRoot(route, rootRoutes: branch.routes) : null; assert( @@ -282,7 +282,7 @@ class RouteConfiguration { for (final MapEntry param in pathParameters.entries) param.key: Uri.encodeComponent(param.value) }; - final String location = route.pattern.toPath(encodedParams); + final String location = route.pattern.toLocation(encodedParams); return Uri( path: location, queryParameters: queryParameters.isEmpty ? null : queryParameters) @@ -502,7 +502,7 @@ class RouteConfiguration { /// Concatenate a Route's pattern with all its ancestor pattern @internal - RoutePattern? buildRoutePatternFromRoot(RouteBase route, + RoutePath? buildRoutePatternFromRoot(RouteBase route, {List? rootRoutes}) { // if the root routes is not provided the top most routes are used rootRoutes ??= _routingConfig.value.routes; @@ -513,9 +513,8 @@ class RouteConfiguration { return null; } - final RoutePattern result = sequence.whereType().fold( - RoutePattern(''), - (RoutePattern prev, GoRoute next) => prev.concatenate(next.pattern)); + final RoutePath result = sequence.whereType().fold(RoutePath(''), + (RoutePath prev, GoRoute next) => prev.concatenate(next.pattern)); return result; } @@ -576,7 +575,7 @@ class RouteConfiguration { final String decorationString = decoration.map((_DecorationType e) => e.toString()).join(); if (route is GoRoute) { - final RoutePattern? fullPattern = buildRoutePatternFromRoot(route); + final RoutePath? fullPattern = buildRoutePatternFromRoot(route); final String? screenName = route.builder?.runtimeType.toString().split('=> ').last; sb.writeln('$decorationString$fullPattern ' diff --git a/packages/go_router/lib/src/match.dart b/packages/go_router/lib/src/match.dart index 3d9b581bd2ab..764cd85404bb 100644 --- a/packages/go_router/lib/src/match.dart +++ b/packages/go_router/lib/src/match.dart @@ -14,7 +14,7 @@ import 'package:meta/meta.dart'; import 'configuration.dart'; import 'logging.dart'; import 'misc/errors.dart'; -import 'route_pattern.dart'; +import 'route_path.dart'; import 'route.dart'; import 'state.dart'; @@ -214,7 +214,7 @@ abstract class RouteMatchBase with Diagnosticable { final Map currentPathParameter = encodedParams.map((String key, String value) => MapEntry(key, Uri.decodeComponent(value))); - final String pathLoc = patternToPath(route.path, encodedParams); + final String pathLoc = route.pattern.toLocation(encodedParams); final String newMatchedLocation = concatenatePaths(matchedLocation, pathLoc); final String newMatchedPath = concatenatePaths(matchedPath, route.path); diff --git a/packages/go_router/lib/src/route.dart b/packages/go_router/lib/src/route.dart index b0069e0abe97..1a7c125ea0b4 100644 --- a/packages/go_router/lib/src/route.dart +++ b/packages/go_router/lib/src/route.dart @@ -11,7 +11,7 @@ import 'package:meta/meta.dart'; import 'configuration.dart'; import 'match.dart'; -import 'route_pattern.dart'; +import 'route_path.dart'; import 'router.dart'; import 'state.dart'; @@ -275,7 +275,7 @@ class GoRoute extends RouteBase { 'builder, pageBuilder, or redirect must be provided'), assert(onExit == null || pageBuilder != null || builder != null, 'if onExit is provided, one of pageBuilder or builder must be provided'), - pattern = RoutePattern(path), + pattern = RoutePath(path), super._(); /// Whether this [GoRoute] only redirects to another route. @@ -449,7 +449,7 @@ class GoRoute extends RouteBase { FlagProperty('redirect', value: redirectOnly, ifTrue: 'Redirect Only')); } - final RoutePattern pattern; + final RoutePath pattern; } /// Base class for classes that act as shells for sub-routes, such @@ -1134,9 +1134,10 @@ class StatefulNavigationShell extends StatefulWidget { /// find the first GoRoute, from which a full path will be derived. final GoRoute route = branch.defaultRoute!; assert(route.pattern.parameters.isEmpty); - final RoutePattern fullPattern = + final RoutePath fullPattern = _router.configuration.buildRoutePatternFromRoot(route)!; - return fullPattern.toPath(shellRouteContext.routerState.pathParameters); + return fullPattern + .toLocation(shellRouteContext.routerState.pathParameters); } } diff --git a/packages/go_router/lib/src/route_pattern.dart b/packages/go_router/lib/src/route_path.dart similarity index 76% rename from packages/go_router/lib/src/route_pattern.dart rename to packages/go_router/lib/src/route_path.dart index 6f3c224fe8a9..5a7770dd61bb 100644 --- a/packages/go_router/lib/src/route_pattern.dart +++ b/packages/go_router/lib/src/route_path.dart @@ -3,34 +3,38 @@ // found in the LICENSE file. /// Represents a route pattern such as '/books/:bookId' -class RoutePattern { +class RoutePath { + /// Constructs a [RoutePath] given a pattern such as '/books/:bookId' + RoutePath(this._pattern) { + final ({RegExp regExp, List parameters}) result = + _buildRegExp(_pattern); + _patternRegExp = result.regExp; + parameters = result.parameters; + } + static final RegExp _parameterRegExp = RegExp(r':(\w+)(\((?:\\.|[^\\()])+\))?'); + + /// route pattern such as '/books/:bookId' final String _pattern; late final RegExp _patternRegExp; - /// the parameters presents in the pattern. - /// Such that RoutePattern('/users/:userId/books/:bookId') + /// The parameters presents in the pattern, + /// such that RoutePath('/users/:userId/books/:bookId') /// will have as parameters ['userId', 'bookId'] late final List parameters; - RoutePattern(this._pattern) { - final ({RegExp regExp, List parameters}) result = - _buildRegExp(_pattern); - _patternRegExp = result.regExp; - parameters = result.parameters; - } - - RegExpMatch? match(String path) { - return _patternRegExp.matchAsPrefix(path) as RegExpMatch?; + /// matches a location against this route path + RegExpMatch? match(String location) { + return _patternRegExp.matchAsPrefix(location) as RegExpMatch?; } - /// Constructs the full path by providing the path paramters + /// Constructs the full location by providing the path paramters /// /// Example: /// - /// RoutePattern('/books/:bookgId').toPath({'bookId': 3}) => /books/3 - String toPath(Map pathParameters) { + /// RoutePath('/books/:bookgId').toLocation({'bookId': 3}) => /books/3 + String toLocation(Map pathParameters) { final StringBuffer buffer = StringBuffer(); int start = 0; for (final RegExpMatch match in _parameterRegExp.allMatches(_pattern)) { @@ -56,13 +60,16 @@ class RoutePattern { }; } - /// Concatenates two paths. + /// Concatenates two patterns. /// - /// e.g: pathA = /a, pathB = c/d, concatenatePaths(pathA, pathB) = /a/c/d. - RoutePattern concatenate(RoutePattern next) { - final String nextPattern = - next._pattern.startsWith('/') ? next._pattern : '/${next._pattern}'; - return RoutePattern(_pattern + nextPattern); + /// e.g: pathA = /a, pathB = /c/d, concatenatePaths(pathA, pathB) = /a/c/d. + /// or: pathA = a, pathB = c/d, concatenatePaths(pathA, pathB) = /a/c/d. + RoutePath concatenate(RoutePath next) { + final Iterable segments = [ + ..._pattern.split('/'), + ...next._pattern.split('/') + ].where((String segment) => segment.isNotEmpty); + return RoutePath('/${segments.join('/')}'); } /// Converts a [pattern] such as `/user/:id` into [RegExp]. diff --git a/packages/go_router/test/path_utils_test.dart b/packages/go_router/test/path_utils_test.dart index e3e172d303d2..c2480af1cb89 100644 --- a/packages/go_router/test/path_utils_test.dart +++ b/packages/go_router/test/path_utils_test.dart @@ -3,7 +3,7 @@ // found in the LICENSE file. import 'package:flutter_test/flutter_test.dart'; -import 'package:go_router/src/route_pattern.dart'; +import 'package:go_router/src/route_path.dart'; void main() { test('patternToRegExp without path parameter', () async {