Go Router + Riverpod Tutorial Series 3: Nested Routes with Authentication
Harish 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
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