diff --git a/packages/go_router/lib/src/match.dart b/packages/go_router/lib/src/match.dart index 87f588a3eeef..f3cf856dd66f 100644 --- a/packages/go_router/lib/src/match.dart +++ b/packages/go_router/lib/src/match.dart @@ -256,9 +256,10 @@ abstract class RouteMatchBase with Diagnosticable { assert(uriPathToCompare.startsWith(newMatchedLocationToCompare)); assert(remainingLocation.isNotEmpty); - final String childRestLoc = uri.path.substring( - newMatchedLocation.length + (newMatchedLocation == '/' ? 0 : 1), - ); + final int offset = + newMatchedLocation.length + (newMatchedLocation == '/' ? 0 : 1); + final String childRestLoc = + offset >= uri.path.length ? '' : uri.path.substring(offset); Map?, List>? subRouteMatches; for (final RouteBase subRoute in route.routes) { diff --git a/packages/go_router/test/match_test.dart b/packages/go_router/test/match_test.dart index 1b12d2e71d7c..9b7aeb314da9 100644 --- a/packages/go_router/test/match_test.dart +++ b/packages/go_router/test/match_test.dart @@ -86,6 +86,63 @@ void main() { expect(matches2.length, 1); expect(matches1.first.pageKey, matches2.first.pageKey); }); + + test('ShellRoute with empty child path does not throw RangeError', () { + // Regression test for https://github.com/flutter/flutter/issues/185948 + // When state restoration produces an URI whose path does not start + // with the matched route path, the substring offset in + // _matchByNavigatorKeyForGoRoute must not exceed uri.path.length. + final route = ShellRoute( + builder: _shellBuilder, + routes: [ + GoRoute( + path: '/a', + builder: _builder, + routes: [ + GoRoute(path: 'b', builder: _builder), + ], + ), + ], + ); + final pathParameters = {}; + expect( + () => RouteMatchBase.match( + route: route, + pathParameters: pathParameters, + uri: Uri.parse('/'), + rootNavigatorKey: GlobalKey(), + ), + returnsNormally, + ); + }); + + test('nested route with path shorter than matchedLocation does not throw', () { + // Regression test for https://github.com/flutter/flutter/issues/185948 + // When newMatchedLocation is longer than uri.path, the offset + // computation in _matchByNavigatorKeyForGoRoute must be clamped. + final route = ShellRoute( + builder: _shellBuilder, + routes: [ + GoRoute( + path: '', + builder: _builder, + routes: [ + GoRoute(path: '', builder: _builder), + ], + ), + ], + ); + final pathParameters = {}; + expect( + () => RouteMatchBase.match( + route: route, + pathParameters: pathParameters, + uri: Uri.parse('/'), + rootNavigatorKey: GlobalKey(), + ), + returnsNormally, + ); + }); }); test('complex parentNavigatorKey works', () {