fix: prevent GoRouter recreation on auth state change
routerProvider was watching authProvider and returning a new GoRouter on every status transition (unknown → authenticated). This caused MaterialApp.router to rebuild the entire navigation tree, which triggered all data providers to start loading before auth was confirmed. Switch to refreshListenable pattern: GoRouter is created once, _RouterNotifier fires notifyListeners() when auth changes, and GoRouter re-runs redirect using ref.read(authProvider). Add /loading splash route shown during AuthStatus.unknown so no authenticated screen (and no API call) is initiated until the stored-token check completes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -22,21 +22,38 @@ import '../../features/products/product_provider.dart';
|
||||
import '../../shared/models/recipe.dart';
|
||||
import '../../shared/models/saved_recipe.dart';
|
||||
|
||||
// Notifies GoRouter when auth state changes without recreating the router.
|
||||
class _RouterNotifier extends ChangeNotifier {
|
||||
_RouterNotifier(Ref ref) {
|
||||
ref.listen<AuthState>(authProvider, (_, __) => notifyListeners());
|
||||
}
|
||||
}
|
||||
|
||||
final routerProvider = Provider<GoRouter>((ref) {
|
||||
final authState = ref.watch(authProvider);
|
||||
final notifier = _RouterNotifier(ref);
|
||||
ref.onDispose(notifier.dispose);
|
||||
|
||||
return GoRouter(
|
||||
initialLocation: '/home',
|
||||
initialLocation: '/loading',
|
||||
refreshListenable: notifier,
|
||||
redirect: (context, state) {
|
||||
// Use ref.read — this runs inside GoRouter, not inside a Riverpod build.
|
||||
final authState = ref.read(authProvider);
|
||||
final isLoggedIn = authState.status == AuthStatus.authenticated;
|
||||
final isAuthRoute = state.matchedLocation.startsWith('/auth');
|
||||
|
||||
if (authState.status == AuthStatus.unknown) return null;
|
||||
// Show splash until the stored-token check completes.
|
||||
if (authState.status == AuthStatus.unknown) return '/loading';
|
||||
if (!isLoggedIn && !isAuthRoute) return '/auth/login';
|
||||
if (isLoggedIn && isAuthRoute) return '/home';
|
||||
return null;
|
||||
},
|
||||
routes: [
|
||||
// Splash shown while auth status is unknown.
|
||||
GoRoute(
|
||||
path: '/loading',
|
||||
builder: (_, __) => const _SplashScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/auth/login',
|
||||
builder: (_, __) => const LoginScreen(),
|
||||
@@ -118,6 +135,15 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
);
|
||||
});
|
||||
|
||||
class _SplashScreen extends StatelessWidget {
|
||||
const _SplashScreen();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => const Scaffold(
|
||||
body: Center(child: CircularProgressIndicator()),
|
||||
);
|
||||
}
|
||||
|
||||
class _InvalidRoute extends StatelessWidget {
|
||||
const _InvalidRoute();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user