diff --git a/flutter_modular/lib/src/presenter/navigation/modular_route_information_parser.dart b/flutter_modular/lib/src/presenter/navigation/modular_route_information_parser.dart index 419e57ef..1abddabf 100644 --- a/flutter_modular/lib/src/presenter/navigation/modular_route_information_parser.dart +++ b/flutter_modular/lib/src/presenter/navigation/modular_route_information_parser.dart @@ -133,7 +133,13 @@ class ModularRouteInformationParser }); return fistTrying.map(_routeSuccess).recover((modularError) { - final params = RouteParmsDTO(url: '$path/', arguments: arguments); + var uri = Uri.parse(path); + if (uri.path.endsWith('/')) { + uri = uri.replace(path: uri.path.substring(0, uri.path.length - 1)); + } else { + uri = uri.replace(path: '${uri.path}/'); + } + final params = RouteParmsDTO(url: uri.toString(), arguments: arguments); return getRoute .call(params) // .map(_routeSuccess) diff --git a/flutter_modular/lib/src/presenter/navigation/modular_router_delegate.dart b/flutter_modular/lib/src/presenter/navigation/modular_router_delegate.dart index db70942f..4ee6709d 100644 --- a/flutter_modular/lib/src/presenter/navigation/modular_router_delegate.dart +++ b/flutter_modular/lib/src/presenter/navigation/modular_router_delegate.dart @@ -115,12 +115,25 @@ class ModularRouterDelegate extends RouterDelegate final page = route.settings as ModularPage; final parallel = page.route; parallel.popCallback?.call(result); - currentConfiguration?.routes.remove(parallel); - if (currentConfiguration?.routes.indexWhere( - (element) => element.uri.toString() == parallel.uri.toString()) == - -1) { - reportPop.call(parallel); + + // Remove the current route and all child routes that depend on it + final routesToRemove = []; + routesToRemove.add(parallel); + + // Find all child routes that have this route as parent + final parallelPath = parallel.uri.path; + for (final route in currentConfiguration?.routes ?? []) { + if (route.parent == parallelPath && route != parallel) { + routesToRemove.add(route); + } } + + // Remove all routes and report them + for (final routeToRemove in routesToRemove) { + currentConfiguration?.routes.remove(routeToRemove); + reportPop.call(routeToRemove); + } + final arguments = parser.getArguments().getOrElse((l) => ModularArguments.empty()); parser.setArguments(arguments.copyWith(uri: currentConfiguration!.uri)); diff --git a/flutter_modular/test/src/presenter/navigation/modular_route_information_parser_test.dart b/flutter_modular/test/src/presenter/navigation/modular_route_information_parser_test.dart index d2da93d3..3bb14515 100644 --- a/flutter_modular/test/src/presenter/navigation/modular_route_information_parser_test.dart +++ b/flutter_modular/test/src/presenter/navigation/modular_route_information_parser_test.dart @@ -89,6 +89,7 @@ void main() { final uri = Uri.parse('/test'); when(() => routeMock.uri).thenReturn(uri); when(() => routeMock.parent).thenReturn(''); + when(() => routeMock.name).thenReturn('/test'); // Add this line when(() => routeMock.middlewares).thenReturn([]); when(() => getRoute.call(any())) .thenAnswer((_) async => Success(routeMock)); @@ -335,6 +336,55 @@ void main() { expect(book.uri.toString(), '/parent/test'); expect(book.chapters().first.name, '/parent'); }); + + test('selectRoute recover with query parameters', () { + final args = ModularArguments.empty(); + + final routeMock = ParallelRouteMock(); + when(() => routeMock.uri).thenReturn(Uri.parse('/test/')); + when(() => routeMock.parent).thenReturn(''); + when(() => routeMock.schema).thenReturn(''); + when(() => routeMock.middlewares).thenReturn([]); + + when(() => reportPush(routeMock)).thenReturn(const Success(unit)); + + when(() => getRoute.call(RouteParmsDTO(url: '/test?a=0', arguments: args))) + .thenAnswer((_) async => Failure(ModularPageException(''))); + when(() => getRoute.call(RouteParmsDTO(url: '/test/?a=0', arguments: args))) + .thenAnswer((_) async => Success(routeMock)); + when(() => getArguments.call()).thenReturn(Success(args)); + + when(() => setArguments.call(any())).thenReturn(const Success(unit)); + + expect(parser.selectRoute('/test?a=0', arguments: args), completes); + }); + + test('selectRoute with resolver route with /', () async { + final args = ModularArguments.empty(); + + final routeMock = ParallelRouteMock(); + when(() => routeMock.uri).thenReturn(Uri.parse('/test')); + when(() => routeMock.parent).thenReturn(''); + when(() => routeMock.name).thenReturn(''); + when(() => routeMock.schema).thenReturn(''); + when(() => routeMock.middlewares).thenReturn([]); + + when(() => reportPush(routeMock)).thenReturn(const Success(unit)); + + when(() => getRoute.call(RouteParmsDTO(url: '/test', arguments: args))) + .thenAnswer((_) async => Success(routeMock)); + when(() => getRoute.call(RouteParmsDTO(url: '/test/', arguments: args))) + .thenAnswer((_) async => Failure(ModularPageException(''))); + when(() => getArguments.call()).thenReturn(Success(args)); + + when(() => setArguments.call(any())).thenReturn(const Success(unit)); + + final route = await parser.selectRoute('/test', arguments: args); + expect(route.uri.toString(), '/test'); + + final route1 = await parser.selectRoute('/test/', arguments: args); + expect(route1.uri.toString(), '/test'); + }); } class Guard extends RouteGuard { diff --git a/flutter_modular/test/src/presenter/navigation/modular_router_delegate_test.dart b/flutter_modular/test/src/presenter/navigation/modular_router_delegate_test.dart index 7f657189..b66100ea 100644 --- a/flutter_modular/test/src/presenter/navigation/modular_router_delegate_test.dart +++ b/flutter_modular/test/src/presenter/navigation/modular_router_delegate_test.dart @@ -132,6 +132,7 @@ void main() { final route = RouteMock(); final parallel = ParallelRouteMock(); when(() => parallel.uri).thenReturn(Uri.parse('/')); + when(() => parallel.parent).thenReturn(''); // Add this line final page = ModularPage( route: parallel, args: ModularArguments.empty(), flags: ModularFlags()); when(() => route.didPop(null)).thenReturn(true); @@ -159,6 +160,7 @@ void main() { final route = RouteMock(); final parallel = ParallelRouteMock(); when(() => parallel.uri).thenReturn(Uri.parse('/')); + when(() => parallel.parent).thenReturn(''); // Add this line final page = ModularPage( route: parallel, args: ModularArguments.empty(), flags: ModularFlags()); when(() => route.didPop(null)).thenReturn(true); @@ -172,12 +174,15 @@ void main() { when(() => childParallel.uri).thenReturn(Uri.parse('/child')); when(() => childParallel.parent).thenReturn('/'); final childPage = ModularPage( - route: childParallel, args: ModularArguments.empty(), flags: ModularFlags()); + route: childParallel, + args: ModularArguments.empty(), + flags: ModularFlags()); when(() => childRoute.didPop(null)).thenReturn(true); when(() => childRoute.settings).thenReturn(childPage); when(() => childRoute.isFirst).thenReturn(false); - when(() => reportPopMock.call(childParallel)).thenReturn(const Success(unit)); + when(() => reportPopMock.call(childParallel)) + .thenReturn(const Success(unit)); final arguments = ModularArguments.empty(); final getArgsMock = GetArgumentsMock(); @@ -188,7 +193,8 @@ void main() { when(getArgsMock.call).thenReturn(Success(arguments)); when(() => setArgsMock.call(any())).thenReturn(const Success(unit)); - delegate.currentConfiguration = ModularBook(routes: [parallel, childParallel]); + delegate.currentConfiguration = + ModularBook(routes: [parallel, childParallel]); expect(delegate.currentConfiguration?.routes.length, 2); delegate.onPopPage(route, null); expect(delegate.currentConfiguration?.routes.length, 0);