diff --git a/example/lib/main.dart b/example/lib/main.dart index efce4a049..09e8f4580 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -53,6 +53,10 @@ class MyApp extends StatelessWidget { name: '/third', page: () => const Third(), ), + GetPage( + name: '/fourth', + page: () => const Fourth(), + ), ], debugShowCheckedModeBanner: false, ); @@ -172,8 +176,7 @@ class Third extends StatelessWidget { width: 300, child: ElevatedButton( onPressed: () { - Get.until((route) { - print(Get.currentRoute); + Get.offNamedUntil('/fourth', (route) { return Get.currentRoute == '/first'; }); }, @@ -184,3 +187,29 @@ class Third extends StatelessWidget { ); } } + +class Fourth extends StatelessWidget { + const Fourth({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.red, + appBar: AppBar( + title: const Text('page four'), + ), + body: Center( + child: SizedBox( + height: 300, + width: 300, + child: ElevatedButton( + onPressed: () { + Get.back(); + }, + child: const Text('go to first screen'), + ), + ), + ), + ); + } +} diff --git a/lib/get_navigation/src/routes/get_router_delegate.dart b/lib/get_navigation/src/routes/get_router_delegate.dart index 9e528133c..d05023454 100644 --- a/lib/get_navigation/src/routes/get_router_delegate.dart +++ b/lib/get_navigation/src/routes/get_router_delegate.dart @@ -528,11 +528,11 @@ class GetDelegate extends RouterDelegate final newPredicate = predicate ?? (route) => false; - while (_activePages.length > 1 && newPredicate(_activePages.last.route!)) { + while (_activePages.length > 1 && !newPredicate(_activePages.last.route!)) { _activePages.removeLast(); } - return _replaceNamed(route); + return _push(route); } @override diff --git a/lib/get_navigation/src/routes/observers/route_observer.dart b/lib/get_navigation/src/routes/observers/route_observer.dart index 205cd2e83..022b1872b 100644 --- a/lib/get_navigation/src/routes/observers/route_observer.dart +++ b/lib/get_navigation/src/routes/observers/route_observer.dart @@ -41,11 +41,6 @@ class GetObserver extends NavigatorObserver { final currentRoute = _RouteData.ofRoute(route); final newRoute = _RouteData.ofRoute(previousRoute); - // if (currentRoute.isSnackbar) { - // // Get.log("CLOSE SNACKBAR ${currentRoute.name}"); - // Get.log("CLOSE SNACKBAR"); - // } else - if (currentRoute.isBottomSheet || currentRoute.isDialog) { Get.log("CLOSE ${currentRoute.name}"); } else if (currentRoute.isGetPageRoute) { @@ -71,13 +66,10 @@ class GetObserver extends NavigatorObserver { value.route = previousRoute; value.isBack = true; value.removed = ''; - // value.isSnackbar = newRoute.isSnackbar; value.isBottomSheet = newRoute.isBottomSheet; value.isDialog = newRoute.isDialog; }); - // print('currentRoute.isDialog ${currentRoute.isDialog}'); - routing?.call(_routeSend); } @@ -86,11 +78,6 @@ class GetObserver extends NavigatorObserver { super.didPush(route, previousRoute); final newRoute = _RouteData.ofRoute(route); - // if (newRoute.isSnackbar) { - // // Get.log("OPEN SNACKBAR ${newRoute.name}"); - // Get.log("OPEN SNACKBAR"); - // } else - if (newRoute.isBottomSheet || newRoute.isDialog) { Get.log("OPEN ${newRoute.name}"); } else if (newRoute.isGetPageRoute) { @@ -99,7 +86,6 @@ class GetObserver extends NavigatorObserver { RouterReportManager.instance.reportCurrentRoute(route); _routeSend!.update((value) { - // Only PageRoute is allowed to change current value if (route is PageRoute) { value.current = newRoute.name ?? ''; } @@ -127,15 +113,16 @@ class GetObserver extends NavigatorObserver { super.didRemove(route, previousRoute); final routeName = _extractRouteName(route); final currentRoute = _RouteData.ofRoute(route); + final previousRouteName = _extractRouteName(previousRoute); Get.log("REMOVING ROUTE $routeName"); + Get.log("PREVIOUS ROUTE $previousRouteName"); _routeSend?.update((value) { value.route = previousRoute; value.isBack = false; value.removed = routeName ?? ''; - value.previous = routeName ?? ''; - // value.isSnackbar = currentRoute.isSnackbar ? false : value.isSnackbar; + value.previous = previousRouteName ?? ''; value.isBottomSheet = currentRoute.isBottomSheet ? false : value.isBottomSheet; value.isDialog = currentRoute.isDialog ? false : value.isDialog; @@ -172,7 +159,6 @@ class GetObserver extends NavigatorObserver { value.isBack = false; value.removed = ''; value.previous = '$oldName'; - // value.isSnackbar = currentRoute.isSnackbar ? false : value.isSnackbar; value.isBottomSheet = currentRoute.isBottomSheet ? false : value.isBottomSheet; value.isDialog = currentRoute.isDialog ? false : value.isDialog; @@ -193,7 +179,6 @@ class Routing { String removed; Route? route; bool? isBack; - // bool? isSnackbar; bool? isBottomSheet; bool? isDialog; @@ -204,7 +189,6 @@ class Routing { this.removed = '', this.route, this.isBack, - // this.isSnackbar, this.isBottomSheet, this.isDialog, }); diff --git a/lib/get_navigation/src/routes/route_middleware.dart b/lib/get_navigation/src/routes/route_middleware.dart index 58f0426d5..7fcd4ac3a 100644 --- a/lib/get_navigation/src/routes/route_middleware.dart +++ b/lib/get_navigation/src/routes/route_middleware.dart @@ -11,6 +11,8 @@ import '../../../get.dart'; abstract class GetMiddleware { GetMiddleware({this.priority = 0}); + final bool _wasInitiated = false; + /// The Order of the Middlewares to run. /// /// {@tool snippet} @@ -99,60 +101,67 @@ abstract class GetMiddleware { } class MiddlewareRunner { - MiddlewareRunner(this._middlewares); + MiddlewareRunner(List? middlewares) + : _middlewares = middlewares != null + ? (List.of(middlewares)..sort(_compareMiddleware)) + : const [] { + // for (final middleware in _middlewares) { + // if (middleware._wasInitiated) continue; + // middleware._wasInitiated = true; + // } + } - final List? _middlewares; + final List _middlewares; - List _getMiddlewares() { - final newMiddleware = _middlewares ?? []; - return List.of(newMiddleware) - ..sort( - (a, b) => (a.priority).compareTo(b.priority), - ); - } + static int _compareMiddleware(GetMiddleware a, GetMiddleware b) => + a.priority.compareTo(b.priority); GetPage? runOnPageCalled(GetPage? page) { - _getMiddlewares().forEach((element) { - page = element.onPageCalled(page); - }); + for (final middleware in _middlewares) { + if (middleware._wasInitiated) { + _middlewares.remove(middleware); + continue; + } + page = middleware.onPageCalled(page); + } return page; } RouteSettings? runRedirect(String? route) { - RouteSettings? to; - for (final element in _getMiddlewares()) { - to = element.redirect(route); - if (to != null) { - break; + for (final middleware in _middlewares) { + final redirectTo = middleware.redirect(route); + if (redirectTo != null) { + return redirectTo; } } - - return to; + return null; } List? runOnBindingsStart(List? bindings) { - _getMiddlewares().forEach((element) { - bindings = element.onBindingsStart(bindings); - }); + for (final middleware in _middlewares) { + bindings = middleware.onBindingsStart(bindings); + } return bindings; } GetPageBuilder? runOnPageBuildStart(GetPageBuilder? page) { - _getMiddlewares().forEach((element) { - page = element.onPageBuildStart(page); - }); + for (final middleware in _middlewares) { + page = middleware.onPageBuildStart(page); + } return page; } Widget runOnPageBuilt(Widget page) { - _getMiddlewares().forEach((element) { - page = element.onPageBuilt(page); - }); + for (final middleware in _middlewares) { + page = middleware.onPageBuilt(page); + } return page; } void runOnPageDispose() { - _getMiddlewares().forEach((element) => element.onPageDispose()); + for (final middleware in _middlewares) { + middleware.onPageDispose(); + } } } diff --git a/test/navigation/get_main_test.dart b/test/navigation/get_main_test.dart index 00b391be8..2604a8a72 100644 --- a/test/navigation/get_main_test.dart +++ b/test/navigation/get_main_test.dart @@ -332,6 +332,120 @@ void main() { expect(find.byType(FirstScreen), findsOneWidget); }); + group('Get.offNamedUntil Tests', () { + testWidgets("Navigates to provided route", (tester) async { + await tester.pumpWidget(WrapperNamed( + initialRoute: '/first', + namedRoutes: [ + GetPage(page: () => const FirstScreen(), name: '/first'), + GetPage(page: () => const SecondScreen(), name: '/second'), + GetPage(page: () => const ThirdScreen(), name: '/third') + ], + )); + + Get.offNamedUntil('/second', (route) => route.name == '/first'); + await tester.pumpAndSettle(); + + expect(find.byType(SecondScreen), findsOneWidget); + expect(Get.currentRoute, '/second'); + }); + + testWidgets("Removes routes that don't match predicate", (tester) async { + await tester.pumpWidget(WrapperNamed( + initialRoute: '/first', + namedRoutes: [ + GetPage(page: () => const FirstScreen(), name: '/first'), + GetPage(page: () => const SecondScreen(), name: '/second'), + GetPage(page: () => const ThirdScreen(), name: '/third') + ], + )); + + Get.toNamed('/second'); + await tester.pumpAndSettle(); + Get.offNamedUntil('/third', (route) => route.name == '/first'); + await tester.pumpAndSettle(); + + expect(find.byType(ThirdScreen), findsOneWidget); + expect(Get.currentRoute, '/third'); + expect(Get.previousRoute, '/first'); + }); + + testWidgets("Keeps routes that match predicate", (tester) async { + await tester.pumpWidget(WrapperNamed( + initialRoute: '/first', + namedRoutes: [ + GetPage(page: () => const FirstScreen(), name: '/first'), + GetPage(page: () => const SecondScreen(), name: '/second'), + GetPage(page: () => const ThirdScreen(), name: '/third'), + ], + )); + + Get.toNamed('/second'); + await tester.pumpAndSettle(); + Get.offNamedUntil('/third', (route) => route.name == '/first'); + await tester.pumpAndSettle(); + Get.back(); + await tester.pumpAndSettle(); + + expect(find.byType(FirstScreen), findsOneWidget); + expect(Get.currentRoute, '/first'); + }); + + testWidgets("Handles predicate that never returns true", (tester) async { + await tester.pumpWidget(WrapperNamed( + initialRoute: '/first', + namedRoutes: [ + GetPage(page: () => const FirstScreen(), name: '/first'), + GetPage(page: () => const SecondScreen(), name: '/second'), + GetPage(page: () => const ThirdScreen(), name: '/third'), + GetPage(page: () => const FourthScreen(), name: '/fourth'), + ], + )); + + Get.toNamed('/second'); + await tester.pumpAndSettle(); + + Get.toNamed('/third'); + await tester.pumpAndSettle(); + + Get.offNamedUntil('/fourth', (route) => false); + await tester.pumpAndSettle(); + + expect(find.byType(FourthScreen), findsOneWidget); + expect(Get.currentRoute, '/fourth'); + expect(Get.previousRoute, '/first'); + }); + + testWidgets("Handles complex navigation scenario", (tester) async { + await tester.pumpWidget(WrapperNamed( + initialRoute: '/first', + namedRoutes: [ + GetPage(page: () => const FirstScreen(), name: '/first'), + GetPage(page: () => const SecondScreen(), name: '/second'), + GetPage(page: () => const ThirdScreen(), name: '/third'), + GetPage(page: () => const FourthScreen(), name: '/fourth'), + ], + )); + + Get.toNamed('/second'); + await tester.pumpAndSettle(); + Get.toNamed('/third'); + await tester.pumpAndSettle(); + Get.offNamedUntil('/fourth', (route) => route.name == '/first'); + await tester.pumpAndSettle(); + + expect(find.byType(FourthScreen), findsOneWidget); + expect(Get.currentRoute, '/fourth'); + expect(Get.previousRoute, '/first'); + + Get.back(); + await tester.pumpAndSettle(); + + expect(find.byType(FirstScreen), findsOneWidget); + expect(Get.currentRoute, '/first'); + }); + }); + testWidgets("Get.offNamedUntil navigates to provided route", (tester) async { await tester.pumpWidget(WrapperNamed( initialRoute: '/first',