goRouter function

  1. @Riverpod.new(keepAlive: true)
GoRouter goRouter(
  1. Ref ref
)

Provider for the GoRouter instance with auth-based routing Keep alive ensures the router is never disposed during app lifetime

Uses StreamListenable to automatically redirect when auth state changes

Implementation

@Riverpod(keepAlive: true)
GoRouter goRouter(Ref ref) {
  final authService = ref.watch(authServiceProvider);

  final refreshListenable = StreamListenable(authService.authStateChanges);
  ref.onDispose(() => refreshListenable.dispose());

  return GoRouter(
    navigatorKey: rootNavigatorKey,
    initialLocation: '/home',
    debugLogDiagnostics: false, // Disabled for performance
    // go_router listens to this and re-evaluates redirects on auth changes
    refreshListenable: refreshListenable,
    redirect: (context, state) {
      final authService = ref.read(authServiceProvider);
      final isAuthenticated = authService.currentUser != null;
      final isGoingToLogin = state.matchedLocation == '/login';

      // Not authenticated → redirect to login, preserving the intended
      // destination so deep links survive the auth flow.
      if (!isAuthenticated && !isGoingToLogin) {
        final destination = state.uri.toString();
        final encoded = Uri.encodeComponent(destination);
        return '/login?redirect=$encoded';
      }

      // Authenticated and on the login page → go to the preserved destination
      // (e.g. a /join/:code deep link) or fall back to home.
      if (isAuthenticated && isGoingToLogin) {
        final redirectTo = state.uri.queryParameters['redirect'];
        if (redirectTo != null && redirectTo.isNotEmpty) {
          return Uri.decodeComponent(redirectTo);
        }
        return '/home';
      }

      // Social is registered-only. Bounce guests reaching it (deep link or
      // post-sign-out race) back home; the upgrade nudge lives in settings.
      final isGuest = authService.currentUser?.isAnonymous ?? false;
      if (isGuest && state.matchedLocation.startsWith('/social')) {
        return '/home';
      }

      return null;
    },
    errorBuilder: (context, state) =>
        NotFoundScreen(location: state.uri.toString()),
    routes: appRoutes,
    observers: ref.watch(navigationObserversProvider),
  );
}