Go Router + Riverpod Tutorial Series 3: Nested Routes with Authentication

Harish KunchalaHarish Kunchala
3 min read

First let's create the required screens.

Settings Page:

Settings page has two buttons: (Go to Account Settings, Logout).

class SettingsPage extends ConsumerWidget {
  const SettingsPage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
      appBar: AppBar(title: const Text('Settings'),),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text ('Settings Page'),
            ElevatedButton(
              onPressed: () => context.go('/home/settings/account'),
              child: const Text('Go to Account Settings'),
            ),
            ElevatedButton(
              onPressed: () => ref.read(authProvider.notifier).logout(),
              child: const Text('Logout'),
            ),
          ]
        )
      )
    );
  }
}

Account Page:

class AccountPage extends ConsumerWidget {
  const AccountPage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
      appBar: AppBar(title: const Text('Account Settings'),),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text ('Account Settings Page'),
            ElevatedButton(
              onPressed: () => ref.read(authProvider.notifier).logout(),
              child: const Text('Logout'),
            ),
          ]
        )
      )
    );
  }
}

Now that we have both the pages.

App Router:

We'll add nested routes for settings and account under /home, ensuring each nested route also has the appropriate authentication guards.

Since we are writing the redirect function guard in every route. I wrote the function redirectIfNotLoggedIn() that can be called from any route.

FutureOr<String?> redirectIfNotLoggedIn(ProviderRef ref) {
  final loggedIn = ref.watch(authProvider);
  if (!loggedIn) {
    return '/login';
  }
  return null;
}

final routerProvider = Provider<GoRouter>((ref) {
  return GoRouter(
      initialLocation: '/home',
      routes: [
        GoRoute(
          path: '/home',
          builder: (context, state) => const HomePage(),
          routes: [
            GoRoute(
              path: 'profile',
              builder: (context, state) => const ProfilePage(),
              redirect: (context, state) => redirectIfNotLoggedIn(ref),
            ),
            GoRoute(
              path: 'settings',
              builder: (context, state) => const SettingsPage(),
              routes: [
                GoRoute(
                  path: 'account',
                  builder: (context, state) => const AccountPage(),
                  redirect: (context, state) => redirectIfNotLoggedIn(ref),
                ),
              ],
              redirect: (context, state) => redirectIfNotLoggedIn(ref),
            ),
          ],
          redirect: (context, state) => redirectIfNotLoggedIn(ref),
        ),
        GoRoute(path: '/login', builder: (context, state) => const LoginPage()),
        GoRoute(
          path: '/admin',
          builder: (context, state) => const AdminPage(),
          redirect: (context, state) => redirectIfNotLoggedIn(ref),
        ),
      ],
      redirect: (context, state) {
        final loggedIn = ref.watch(authProvider);
        final isLoggingIn = state.path == '/login';

        if (!loggedIn && !isLoggingIn) {
          return '/login';
        } else if (loggedIn && isLoggingIn) {
          return '/home';
        }
        return null;
      });
});

Navigation using HomePage

We can add two buttons to go to the ProfilePage and SettingsPage .

class HomePage extends ConsumerWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('GRT Redirect'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('Home Page'),
            const SizedBox(height: 20),
            ElevatedButton(
                onPressed: () {
                  ref.read(authProvider.notifier).logout();
                },
                child: const Text('Logout')),
            const SizedBox(height: 20),
            ElevatedButton(
                onPressed: () {
                  context.go('/home/profile');
                },
                child: const Text('Go to Profile')),
            const SizedBox(height: 20),
            ElevatedButton(
                onPressed: () {
                  context.go('/home/settings');
                },
                child: const Text('Go to Settings')),
          ],
        ),
      ),
    );
  }
}

So let's test the nested routes with redirection

Nested Routes with Redirection Guards

As always you can find the code here for this topic here: nested_routes_with_auth

0
Subscribe to my newsletter

Read articles from Harish Kunchala directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Harish Kunchala
Harish Kunchala