skip to Main Content

I’m using Go Router 6.2.0 with Flutter 3.7.3

Problem: I want to navigate to the shell route when a user is logged in and the login page when no one is logged in.
What developers usually do is redirect the route to another one when user is logged in but I can’t redirect the route to a shell route because I don’t know how

I have the 2 routes in my GoRouter, 1 is the Login route for the login page, in my login page I’m doing this to check for auth then go to the another route accordingly:

  Scaffold(
      body: StreamBuilder(
          stream: FirebaseAuth.instance.authStateChanges(),
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.active) {
              final user = snapshot.data;
              if (user != null) {
                GoRouter.of(context).go(NavigationRailScreen.routeName);
              }
            }
            return Center(
              child: SizedBox(
                width: 400,
                child: ui.SignInScreen(
                  providers: [ui.EmailAuthProvider()],
                  actions: [
                    ui.AuthStateChangeAction<ui.SignedIn>((context, state) {
                      GoRouter.of(context).go(NavigationRailScreen.routeName);
                    }),
                  ],
                ),
              ),
            );
          }),
    );

The second route is a NavigationRail which has all the rest of my routes

final GlobalKey<NavigatorState> _rootNavigator = GlobalKey(debugLabel: 'root');
final GlobalKey<NavigatorState> _shellNavigator =
    GlobalKey(debugLabel: 'shell');

GoRouter router = GoRouter(
  navigatorKey: _rootNavigator,
  initialLocation: '/',
  routes: [
    GoRoute(
      parentNavigatorKey: _rootNavigator,
      path: '/',
      name: 'login',
      builder: (context, state) => AppLoginScreen(key: state.pageKey),
    ),
    ShellRoute(
        navigatorKey: _shellNavigator,
        builder: (context, state, child) =>
            NavigationRailScreen(key: state.pageKey, child: child),
        routes: [
          GoRoute(
            path: '/',
            name: HomeScreen.routeName,
            pageBuilder: (context, state) {
              return NoTransitionPage(child: HomeScreen(key: state.pageKey));
            },
          ),
          GoRoute(
              path: '/restaurants',
              name: RestaurantsScreen.routeName,
              pageBuilder: (context, state) {
                return NoTransitionPage(
                    child: RestaurantsScreen(key: state.pageKey));
              },
              routes: [
                GoRoute(
                  parentNavigatorKey: _shellNavigator,
                  path: ':id',
                  name: RestaurantDetailsScreen.routeName,
                  pageBuilder: (context, state) {
                    final String id = state.params['id'] as String;
                    return NoTransitionPage(
                      child:
                          RestaurantDetailsScreen(id: id, key: state.pageKey),
                    );
                  },
                  routes: [
                    GoRoute(
                      parentNavigatorKey: _shellNavigator,
                      path: AddRestaurantReviewScreen.routeName,
                      name: AddRestaurantReviewScreen.routeName,
                      pageBuilder: (context, state) {
                        final String id = state.params['id'] as String;
                        return NoTransitionPage(
                          child: AddRestaurantReviewScreen(
                              key: state.pageKey, id: id),
                        );
                      },
                    ),
                    GoRoute(
                        name: MenuItemsScreen.routeName,
                        path: MenuItemsScreen.routeName,
                        pageBuilder: (context, state) {
                          return NoTransitionPage(
                            child: MenuItemsScreen(
                                key: state.pageKey, id: state.params['id']!),
                          );
                        },
                        routes: [
                          GoRoute(
                              name: MenuItemDetailsScreen.routeName,
                              path: ':menuItemId',
                              pageBuilder: (context, state) {
                                final String id =
                                    state.params['menuItemId'] as String;
                                final String restaurantId =
                                    state.params['id'] as String;
                                return NoTransitionPage(
                                  child: MenuItemDetailsScreen(
                                    key: state.pageKey,
                                    id: id,
                                    restaurantId: restaurantId,
                                  ),
                                );
                              },
                              routes: [
                                GoRoute(
                                    name: AddMenuItemReviewScreen.routeName,
                                    path: AddMenuItemReviewScreen.routeName,
                                    pageBuilder: (context, state) {
                                      final String id =
                                          state.params['menuItemId'] as String;
                                      final String restaurantId =
                                          state.params['id'] as String;

                                      return NoTransitionPage(
                                          child: AddMenuItemReviewScreen(
                                        key: state.pageKey,
                                        id: id,
                                        restaurantId: restaurantId,
                                      ));
                                    })
                              ]),
                        ]),
                  ],
                ),
              ]),
          GoRoute(
            path: '/profile',
            name: UserProfileScreen.routeName,
            pageBuilder: (context, state) {
              return NoTransitionPage(
                  child: UserProfileScreen(key: state.pageKey));
            },
          )
        ])
  ],
  errorBuilder: (context, state) => RouteErrorScreen(
    errorMsg: state.error.toString(),
    key: state.pageKey,
  ),
);

2

Answers


  1. To Navigate to the shell route, the code is given bellow replace the location with your route name

      GoRouter router = GoRouter.of(context);
                  router.go(location);
    

    For Handling the authentication i use the redirect method of go router
    in which emit the state of current appstatus.

        final _rootNavigatorKey = GlobalKey<NavigatorState>();
    final _shellNavigatorKey = GlobalKey<NavigatorState>();
    
    GoRouter gorouter(AppStatus appStatus) {
      return GoRouter(
        initialLocation: RouteConstants.login,
        navigatorKey: _rootNavigatorKey,
        redirect: (BuildContext context, GoRouterState state) {
          switch (appStatus) {
            case AppStatus.unauthenticated:
              return RouteConstants.login;
            case AppStatus.authenticated:
              if (state.location == RouteConstants.login ||
                  state.location == RouteConstants.signUp) {
                return RouteConstants.dashboard;
              } else {
                return state.location;
              }
          }
        },
        routes: [
          ShellRoute(
            navigatorKey: _shellNavigatorKey,
            pageBuilder: (context, state, child) {
              return NoTransitionPage(
                  child: App(
                location: state.location,
                child: child,
              ));
            },
            routes: [
              GoRoute(
                path: RouteConstants.dashboard,
                parentNavigatorKey: _shellNavigatorKey,
                pageBuilder: (context, state) {
                  return const NoTransitionPage(child: Dashboard());
                },
              ),
              GoRoute(
                path: RouteConstants.history,
                parentNavigatorKey: _shellNavigatorKey,
                pageBuilder: (context, state) {
                  return const NoTransitionPage(child: History());
                },
              ),
            ],
          ),
          GoRoute(
            parentNavigatorKey: _rootNavigatorKey,
            path: RouteConstants.login,
            builder: (context, state) {
              return const SignInScreen();
            },
          ),
          GoRoute(
            parentNavigatorKey: _rootNavigatorKey,
            path: RouteConstants.signUp,
            builder: (context, state) {
              return const SignUpScreen();
            },
          ),
        ],
      );
    }
    

    Main screen

    class AppView extends StatelessWidget {
      const AppView({super.key});
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp.router(
          debugShowCheckedModeBanner: false,
          title: 'Lazy Us',
          theme: lightTheme(),
          darkTheme: darkTheme(),
          routerConfig: gorouter(
            context.select((AppBloc bloc) => bloc.state.status),
          ),
        );
      }
    }
    
    Login or Signup to reply.
  2. I spent several hours trying to resolve this problem, I don’t think it is possible to navigate to a shell route at the moment with GoRoute but however I wa able to find a way that works for me base on the project I am working on. Kindly checkout the code below:

    1. First of all, I added my bottom navigation bar to my route constants.

        static const onboarding = '/';
        static const navigationBar = '/navigationBar';
        static const home = '/home';
        static const unknown = '/unknown';
        static const notifications = '/notifications';
        }```
      
      
    2. Secondly, I created the BottomNavigationScreen.

        final Widget? child;
        const BottomNavigationScreen({super.key, this.child});
      
        @override
        State<BottomNavigationScreen> createState() => _BottomNavigationScreenState();
      }
      
      class _BottomNavigationScreenState extends State<BottomNavigationScreen> {
        int selectedIndex = 0;
      
        @override
        Widget build(BuildContext context) {
          return Scaffold(
            body: widget.child,
            bottomNavigationBar: BottomNavigationBar(
                currentIndex: calculateSelectedIndex(),
                onTap: (value) => setState(() {
                      selectedIndex = value;
                      switch (value) {
                        case 0:
                          return context.go(AppRoutes.home);
                        case 1:
                          return context.go(AppRoutes.unknown);
                        case 2:
                          return context.go(AppRoutes.notifications);
                        case 3:
                          return context.go(AppRoutes.profile);
                      }
                    }),
                type: BottomNavigationBarType.fixed,
                items: List.generate(
                    items.length,
                    (i) => BottomNavigationBarItem(
                        icon: Icon(
                          items[i],
                          size: 30,
                          color: selectedIndex == i
                              ? const Color(0xff5B53FF)
                              : Colors.black,
                        ),
                        label: ''))),
          );
        }
      
        int calculateSelectedIndex() {
          final location = GoRouter.of(context).location;
          switch (location) {
            case AppRoutes.home:
              return 0;
            case AppRoutes.unknown:
              return 1;
            case AppRoutes.notifications:
              return 2;
            case AppRoutes.profile:
              return 3;
            default:
              return 0;
          }
        }
      }
      
      List<IconData> items = [
        Icons.house,
        Icons.confirmation_num_rounded,
        Icons.notifications,
        Icons.account_circle
      ];```
      
      
      
    3. Thirdly, I defined my shell routes and noticed I also added the BottomNavigationScreen among the routes and gave it and initial screen to display. This is the trick I used.

    GoRoute(
           path: AppRoutes.personalInfoScreen3,
           builder: (context, state) => const PersonalInfoScreen3(),
         ),
         GoRoute(
           path: AppRoutes.navigationBar,
           builder: (context, state) =>
               const BottomNavigationScreen(child: HomeScreen()),
         ),
         ShellRoute(
             navigatorKey: _shellNavigatorKey,
             builder: (context, state, child) =>
                 BottomNavigationScreen(child: child),
             routes: [
               GoRoute(
                 path: AppRoutes.home,
                 builder: (context, state) => const HomeScreen(),
               ),
               GoRoute(
                 path: AppRoutes.unknown,
                 builder: (context, state) => const Scaffold(),
               ),
               GoRoute(
                 path: AppRoutes.notifications,
                 builder: (context, state) => const NotificationScreen(),
               ),
               GoRoute(
                 path: AppRoutes.profile,
                 builder: (context, state) => const ProfileScreen(),
               ),
             ])
    
    1. Lastly, we can navigate to the BottomNavigationScreen after signing up or signing into the app.
    CustomButton(onPressed: () => context.push(AppRoutes.navigationBar),
                  text: 'Continue',
                  color: secondaryColor),
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search