@@ -442,6 +442,111 @@ void main() {
442442 expect (goRouter.routerDelegate.currentConfiguration.matches.length, 0 );
443443 expect (goRouter.routerDelegate.canPop (), false );
444444 });
445+
446+ testWidgets ('It should return false on screen A and true on screen B after push' , (
447+ WidgetTester tester,
448+ ) async {
449+ final GoRouter goRouter = GoRouter (
450+ initialLocation: '/a' ,
451+ routes: < GoRoute > [
452+ GoRoute (path: '/a' , builder: (_, _) => const Text ('Screen A' )),
453+ GoRoute (path: '/b' , builder: (_, _) => const Text ('Screen B' )),
454+ ],
455+ );
456+ addTearDown (goRouter.dispose);
457+ await tester.pumpWidget (MaterialApp .router (routerConfig: goRouter));
458+ await tester.pumpAndSettle ();
459+
460+ expect (find.text ('Screen A' ), findsOneWidget);
461+ expect (goRouter.routerDelegate.canPop (), isFalse);
462+
463+ goRouter.push ('/b' );
464+ await tester.pumpAndSettle ();
465+
466+ expect (find.text ('Screen B' ), findsOneWidget);
467+ expect (goRouter.routerDelegate.canPop (), isTrue);
468+
469+ goRouter.pop ();
470+ await tester.pumpAndSettle ();
471+
472+ expect (find.text ('Screen A' ), findsOneWidget);
473+ expect (goRouter.routerDelegate.canPop (), isFalse);
474+ expect (goRouter.routerDelegate.currentConfiguration.matches.length, 1 );
475+ expect (goRouter.routerDelegate.currentConfiguration.uri.path, '/a' );
476+ });
477+
478+ testWidgets ('It should check shell navigator when root navigator cannot pop' , (
479+ WidgetTester tester,
480+ ) async {
481+ final shellNavigatorKey = GlobalKey <NavigatorState >();
482+ final GoRouter goRouter = GoRouter (
483+ initialLocation: '/a' ,
484+ routes: < RouteBase > [
485+ ShellRoute (
486+ navigatorKey: shellNavigatorKey,
487+ builder: (_, _, Widget child) => child,
488+ routes: < GoRoute > [
489+ GoRoute (path: '/a' , builder: (_, _) => const Text ('Screen A' )),
490+ GoRoute (path: '/b' , builder: (_, _) => const Text ('Screen B' )),
491+ ],
492+ ),
493+ ],
494+ );
495+ addTearDown (goRouter.dispose);
496+ await tester.pumpWidget (MaterialApp .router (routerConfig: goRouter));
497+ await tester.pumpAndSettle ();
498+
499+ expect (goRouter.routerDelegate.canPop (), isFalse);
500+ expect (goRouter.routerDelegate.navigatorKey.currentState? .canPop (), isFalse);
501+
502+ goRouter.push ('/b' );
503+ await tester.pumpAndSettle ();
504+
505+ expect (goRouter.routerDelegate.canPop (), isTrue);
506+ expect (shellNavigatorKey.currentState? .canPop (), isTrue);
507+ expect (goRouter.routerDelegate.navigatorKey.currentState? .canPop (), isFalse);
508+
509+ goRouter.pop ();
510+ await tester.pumpAndSettle ();
511+
512+ expect (find.text ('Screen A' ), findsOneWidget);
513+ expect (goRouter.routerDelegate.canPop (), isFalse);
514+ expect (shellNavigatorKey.currentState? .canPop (), isFalse);
515+ });
516+ });
517+
518+ group ('willHandlePopInternally' , () {
519+ testWidgets ('does not remove route when page handles pop internally' , (
520+ WidgetTester tester,
521+ ) async {
522+ final GoRouter goRouter = GoRouter (
523+ initialLocation: '/' ,
524+ routes: < GoRoute > [
525+ GoRoute (
526+ path: '/' ,
527+ builder: (_, _) => const Text ('Home' ),
528+ routes: < GoRoute > [
529+ GoRoute (
530+ path: 'internal' ,
531+ pageBuilder: (_, _) => const _InternalPopPage (),
532+ ),
533+ ],
534+ ),
535+ ],
536+ );
537+ addTearDown (goRouter.dispose);
538+ await tester.pumpWidget (MaterialApp .router (routerConfig: goRouter));
539+
540+ goRouter.push ('/internal' );
541+ await tester.pumpAndSettle ();
542+ expect (find.text ('Internal' ), findsOneWidget);
543+
544+ goRouter.pop ();
545+ await tester.pumpAndSettle ();
546+
547+ expect (find.text ('Internal' ), findsOneWidget);
548+ expect (find.text ('Home' ), findsNothing);
549+ });
445550 });
446551
447552 group ('pushReplacement' , () {
@@ -752,3 +857,45 @@ class _DummyStatefulWidgetState extends State<DummyStatefulWidget> {
752857 @override
753858 Widget build (BuildContext context) => Container ();
754859}
860+
861+ class _InternalPopPage extends Page <void > {
862+ const _InternalPopPage ();
863+
864+ @override
865+ Route <void > createRoute (BuildContext context) => _InternalPopRoute (this );
866+ }
867+
868+ class _InternalPopRoute extends PageRoute <void > {
869+ _InternalPopRoute (_InternalPopPage page) : super (settings: page);
870+
871+ @override
872+ Color ? get barrierColor => null ;
873+
874+ @override
875+ String ? get barrierLabel => null ;
876+
877+ @override
878+ bool get opaque => true ;
879+
880+ @override
881+ bool get maintainState => true ;
882+
883+ @override
884+ Duration get transitionDuration => Duration .zero;
885+
886+ @override
887+ bool get willHandlePopInternally => true ;
888+
889+ @override
890+ // ignore: must_call_super
891+ bool didPop (void result) => false ;
892+
893+ @override
894+ Widget buildPage (
895+ BuildContext context,
896+ Animation <double > animation,
897+ Animation <double > secondaryAnimation,
898+ ) {
899+ return const Text ('Internal' );
900+ }
901+ }
0 commit comments