flutterflutter-go-router

Flutter - How do I correctly handle go_router, with redirects and context.pop()


I'm using go_router and trying to handle auth using redirects, so if a user isn't logged in they go to the inviteCode screen. This all works well.

My flow is: invite screen -> home -> profile

The profile screen has a log out button on it.

My problem is that when the user logs out they return to the inviteCode screen correctly, but if they press the android back button, they end up at the home screen which should only be access when authenticated. (Note the same happens if I add a button with context.pop() onto that original screen)

Here's my GoRouter config

    /// Forwards diagnostic messages to the dart:developer log() API.
    debugLogDiagnostics: true,
    errorBuilder: (context, state) => ErrorPage(state),
    initialLocation: '/',
    redirect: (BuildContext context, GoRouterState state) {
      User user = context.read(userStateProvider);
      if (!user.isLoggedIn) {
        return '/welcome/inviteCode';
      } else {
        return null;
      }
    },

    routes: <RouteBase>[
      GoRoute(
        path: '/',
        builder: (BuildContext context, GoRouterState state) {
          return const HomeScreen();
        },
        routes: <RouteBase>[
          GoRoute(
            path: 'profile',
            builder: (BuildContext context, GoRouterState state) {
              return const ProfileScreen();
            },
          ),
          GoRoute(
            path: 'welcome/inviteCode',
            builder: (BuildContext context, GoRouterState state) {
              return const InviteCodeScreen();
            },
          )
        ],
      ),
    ],
  );

Does anyone know what I should be doing to stop this happening. It feels like a pretty common usage pattern that I'd imagine lots of other apps use with it, but I'm finding the documentation hard to follow/outdated


Solution

  • That is the expected behavior based on your routes hierarchy structure. GoRouter uses declarative routing (aka Flutter Navigator 2.0). Navigating to a destination using declarative routing will replace the current stack of "screens" with the stack of "screens" configured to be displayed for the destination route.

    On your routes configuration the route with path "welcome/inviteCode" is a child of the route "/". When you navigate using "go" to "/welcome/inviteCode" it will build the stack of routes bellow:

    |- HomeScreen
    |  |- InviteCodeScreen
    

    Which means that if you "pop back" from that InviteCodeScreen screen it will show the HomeScreen screen, since it's the page that is "bellow" it in the stack of routes.

    If you want you InviteCodeScreen to be a root route, you shall move it up in the routes configuration hierarchy and put it at the same level as HomeScreen:

    routes: <RouteBase>[
          GoRoute(
            path: '/',
            builder: (BuildContext context, GoRouterState state) {
              return const HomeScreen();
            },
            routes: <RouteBase>[
              GoRoute(
                path: 'profile',
                builder: (BuildContext context, GoRouterState state) {
                  return const ProfileScreen();
                },
              ),
            ],
          ),
          GoRoute(
                path: '/welcome/inviteCode',
                builder: (BuildContext context, GoRouterState state) {
                  return const InviteCodeScreen();
                },
              )
        ],
    

    IMPORTANT: The path is now /welcome/inviteCode instead of just welcome/inviteCode since it is no longer a child route of /