Seamless Auto-Redirect Authentication with Firebase, Riverpod Generator and GoRouter
In any modern app, managing authentication is essential. However, it’s not just about verifying the user’s identity; efficiently directing users to the right page based on their authentication state is crucial for a seamless flow. Imagine a user opening your app: if they are logged in, they should be taken directly to the home page. If not, they should be redirected to the login or signup page. This auto-redirection ensures that users are always taken to the right place without unnecessary steps, improving navigation and reducing confusion.

1. First, let’s listen to the current user with firebase
@Riverpod(keepAlive: true)
class UserNotifier extends _$UserNotifier {
@override
Stream<User?> build() => FirebaseAuth.instance.authStateChanges();
}
With this provider, we can monitor the authentication state at any time. It taps into Firebase’s authStateChanges()
stream, which emits the current user state whenever the authentication status changes (e.g., when a user logs in, logs out).
2. Create AsyncValueNotifier
By default, GoRouter’s refreshListenable
does not support AsyncValue
directly, as it only works with ChangeNotifier
or other Listenable types. This poses a challenge when using Riverpod’s AsyncValue
to manage the authentication state, but there’s a simple solution: creating a ChangeNotifier
that wraps the AsyncValue
so that GoRouter can refresh whenever the authentication state changes.
class AsyncValueNotifier<T> extends ChangeNotifier {
AsyncValueNotifier(this._asyncValue);
AsyncValue<T> _asyncValue;
AsyncValue<T> get asyncValue => _asyncValue;
void update(AsyncValue<T> newValue) {
if (newValue != _asyncValue) {
_asyncValue = newValue;
notifyListeners();
}
}
}
3. Create Provider for the ChangeNotifier
To ensure that GoRouter stays in sync with the current user’s authentication state, we need to create a Provider
that supplies and updates the ChangeNotifier
we just created. This provider will be responsible for providing the AsyncValueNotifier
to GoRouter, which in turn will trigger automatic redirection when the user’s state changes.
@Riverpod(keepAlive: true)
Raw<AsyncValueNotifier<User?>> currentUserNotifier(CurrentUserNotifierRef ref) {
final asyncValue = ref.watch(userNotifierProvider);
final notifier = AsyncValueNotifier<User?>(asyncValue);
ref.listen<AsyncValue<User?>>(userNotifierProvider, (previous, next) {
notifier.update(next);
});
return notifier;
}
4. Create the router
Now that we have set up the AsyncValueNotifier
and the provider, we can go ahead and integrate GoRouter with Riverpod. The key step here is to make GoRouter aware of changes in the user’s authentication state by watching the currentUserNotifier
we created. This way, GoRouter will automatically handle navigation updates whenever the authentication state changes.
part 'router.g.dart';
final routerKey = GlobalKey<NavigatorState>();
@Riverpod(keepAlive: true)
GoRouter router(RouterRef ref) {
final user = ref.watch(currentUserNotifierProvider);
return GoRouter(
navigatorKey: routerKey,
refreshListenable: user,
initialLocation: AuthRoute().location,
routes: $appRoutes,
redirect: (context, state) {
final isLogged = user.asyncValue.value != null;
if (state.uri.path == AuthRoute().location && isLogged) return HomeRoute().location;
if (!isLogged) return AuthRoute().location;
return null;
},
);
}
5. Create MaterialApp
class MyApp extends HookConsumerWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final router = ref.watch(routerProvider);
return MaterialApp.router(
debugShowCheckedModeBanner: false,
routeInformationParser: router.routeInformationParser,
routeInformationProvider: router.routeInformationProvider,
routerDelegate: router.routerDelegate,
);
}
}
This setup ensures that your app always shows the correct screen based on the user’s authentication state, with GoRouter automatically handling redirection when needed.
6. Conclusion
Implementing seamless auto-redirect authentication in a Flutter app can significantly improve the user experience by ensuring that users are always routed to the appropriate page based on their authentication status. By leveraging Firebase for authentication, Riverpod for state management, and GoRouter for routing, we’ve created a flexible, scalable solution that can dynamically adjust the navigation flow as the authentication state changes.
With this approach, you can ensure that your app handles authentication efficiently, while maintaining a clean and responsive navigation structure. Whether you’re working on a small app or a large project, this setup will help keep your users’ sessions secure and their experience smooth.