1

I'm trying to learn to auto_route and its features. Now I'm stuck in navigation guards, I'm using Riverpod for state management. Here I wanted to implement an authGuard for navigating users to different screens if user is not logged in.

class AppRouter extends _$AppRouter {
 @override
 List<AutoRoute> get routes => [
    AutoRoute(
      page: SplashRoute.page,
      initial: true,
    ),
    AutoRoute(page: StarredRepoRoute.page, guards: [AuthGuard]),
    AutoRoute(page: SignInRoute.page),
    AutoRoute(page: AuthorizationRoute.page),
  ];

bool isInitialRoute() {
return false;
}
}

The above-pasted code is the AppRouter and you can see that I have mentioned guards for StarredRepoRoute and the guard is AuthGuard.

class AuthGuard extends AutoRouteGuard {
 final Ref _ref;
 AuthGuard(this._ref);
 @override
 void onNavigation(NavigationResolver resolver, StackRouter router) async {
   final authState = _ref.watch(authNotifierProvider);
   authState.maybeMap(
    orElse: () => false,
    authenticated: (_) => true,
  );
 }
}

If the onNavigation return true it will navigate to StarredRepoRoute. I also settled up an authGuardProvider for it.

final authGuardProvider = Provider<AuthGuard>((ref) {
  return AuthGuard(ref);
});

But the issue is that when I refer to guard as AuthGuard as appRouter it says

enter image description here

Is there anything I miss or is there any other way to implement it?

Akhil
  • 419
  • 5
  • 15

3 Answers3

0

We assign guards to route by doing the below:

AutoRoute(page: StarredRepoRoute.page, guards: [AuthGuard()]),

and not

AutoRoute(page: StarredRepoRoute.page, guards: [AuthGuard]),
Codefarmer
  • 684
  • 4
  • 8
0

I have solved this issue with GetIt Dependency Injection.

Instead of Ref the AuthGuard depends on ProviderContainer:

class AuthGuard extends AutoRouteGuard {
  final ProviderContainer providerContainer;
 AuthGuard(this.providerContainer);
 @override
 void onNavigation(NavigationResolver resolver, StackRouter router) async {
   final authState = providerContainer.read(authNotifierProvider);
   authState.maybeMap(
    orElse: () => false,
    authenticated: (_) => true,
  );
 }
}

The providerContainer can be a static variable somewhere in your project and should be explicitly set in the UncontrolledProviderScope:

final ProviderContainer providerContainer = ProviderContainer()
   
...

  runApp(UncontrolledProviderScope(
    container: providerContainer,
    child: const App(),
  ));

The AuthGuard has to be registered as a Singleton in your GetIt serviceRegistry.

final GetIt serviceRegistry = GetIt.instance;

...

serviceRegistry.registerSingleton(AuthGuard(providerContainer))

Now you can access the instance of AuthGuard via GetIt like this:

AutoRoute(page: StarredRepoRoute.page, guards: [serviceRegistry<AuthGuard>()]),
0

RiverPod already serves as replacement for dependency injection I think you don't need to add another one. Have you tried passing the WidgetRef through AppRoute constructor?

class MainApp extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return MaterialApp.router(
        routerConfig: AppRouter(ref).config(),
    ...

on the AppRoute file do:

@AutoRouterConfig()
class AppRouter extends $AppRouter {
  WidgetRef ref;

  AppRouter(this.ref) : super();

  @override
  List<AutoRoute> get routes => [
        AutoRoute(path: '/', page: LoginRoute.page),
        AutoRoute(
          path: '/main-nav',
          page: MainNav.page,
          guards: [AuthGuard(ref)],
        ),
      ];
}

and finally on the GuardRoute do:

class AuthGuard extends AutoRouteGuard {
  final WidgetRef ref;

  AuthGuard(this.ref);

  @override
  void onNavigation(NavigationResolver resolver, StackRouter router) async {
    UserDTO? user = ref.watch(userServiceProvider.notifier).getLoggedUser();

    if (user != null) {
      resolver.next(true);
    } else {
      router.push(const LoginRoute());
    }
  }
}
Rinaldi Segecin
  • 449
  • 8
  • 23