If you are using an authState provider to check if user is logged in or not and redirect him, using go_router is quite tricky.
When I started using the redirect method of GoRouter it seems really easy to manage, but then I realized that the hot reload always redirected me to the initialRoute '/'.
In my project I use riverpod to check for the authState (ref.watch(currentUserAccountProvider)). But to use this code in the GoRouter config, the config must be dynamic (vs static). And it result of re-initializing GoRouter config each time your app is rebuilt (-> redirect to "/")
So here is my solution. I am quite new to Flutter so I don't know if it's a good practice or not, experienced users could say, but at least it works for me.
GoRouter config file :
/// The route configuration.
class MyRouterConfig {
MyRouterConfig();
GoRouter get router => _router;
static final GoRouter _router = GoRouter(
routes: <RouteBase>[
GoRoute(
path: '/signin',
builder: (BuildContext context, GoRouterState state) {
return const SignInView();
},
),
GoRoute(
path: '/signup',
builder: (BuildContext context, GoRouterState state) {
return const SignUpView();
},
),
GoRoute(
path: '/',
builder: (BuildContext context, GoRouterState state) {
return const HomeView();
},
),
GoRoute(
path: '/otherview',
builder: (BuildContext context, GoRouterState state) {
return const OtherView();
},
),
],
routerNeglect: true,
);
}
Then I created a widget to manage the redirection following the authState: CheckAuthWidget :
class CheckAuthWidget extends ConsumerWidget {
final Widget child;
const CheckAuthWidget({super.key, required this.child});
@override
Widget build(BuildContext context, WidgetRef ref) {
final currentUserAccount = ref.watch(currentUserAccountProvider);
final GoRouter router = MyRouterConfig().router; // Get the router config directly from MyRouterConfig instead of GoRouter.of(context)
final currentLocation = router.routerDelegate.currentConfiguration.fullPath;
final List<String> authorizedRoutes = ['/signin', '/signup'];
currentUserAccount.when(
data: (user) {
// If the user is not connected -> redirect to /signin
if (user == null && !authorizedRoutes.contains(currentLocation)) {
router.go('/signin');
}
if (user != null &&
(currentLocation == '/signin' || currentLocation == '/signup')) {
router.go('/');
}
},
loading: () {},
error: (error, stackTrace) {},
);
return child;
}
}
Here is my main :
void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends ConsumerWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return MaterialApp.router(
title: 'Family Menu',
routerDelegate: MyRouterConfig().router.routerDelegate,
routeInformationParser:
MyRouterConfig().router.routeInformationParser,
routeInformationProvider:
MyRouterConfig().router.routeInformationProvider,
scaffoldMessengerKey: scaffoldMessengerKey,
supportedLocales: AppLocalizations.supportedLocales,
localizationsDelegates: AppLocalizations.localizationsDelegates,
debugShowCheckedModeBanner: false,
builder: (context, child) => CheckAuthWidget(child: child!),
);
}
}
I hope this could help someone else, and please tell me if I am doing something wrong :)