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

Search for a command to run...

No comments yet. Be the first to comment.
In here we'll look at a series of tutorials covering Go Router and Riverpod together.
First let's update our AuthNotifier to support state restoration Source code: redirection_with_state_restoration Prerequisites: First we are going to use flutter_secure_storage to store the state of the app. So lets add it our pubspec.yaml flutter pu...
While I generally write about Flutter. This problem has been bugging me for a while and there seem to be limited resources to find a solution. So for anyone who is looking for a solution. Here you go: Type edge:flags into the address bar and open it...
In this tutorial, we’ll go through covering interceptor, which is a very crucial feature of Dio. You can find the entire source code for the project here: dio_tasker Understanding Interceptors: What is an interceptor? An interceptor in Dio is a powe...

In this part, we’ll enhance our Task Manager app by parsing JSON responses and handling different types of errors. We’ll use Dio’s built-in error handling mechanisms. You can find the source code of the entire app here: dio_tasker Parsing JSON Respon...

While building apps in Flutter is extremely fluid and wonderfully intuitive. We obviously know that we can’t build the next TikTok or Twitter by only building offline apps. Which is why we absolutely need internet capabilities in our Flutter apps. Th...

Understanding when to use setState in Flutter is crucial for managing our app’s state effectively. Here’s a detailed guide: When to use setState ? Updating the UI: Use setState when we need to update the UI in response to changes in the internal sta...

First let's create the required screens.
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'),
),
]
)
)
);
}
}
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.
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;
});
});
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