使用AutoRoute与GetIt的认证服务重定向异常问题排查
问题:AutoRoute + GetIt 认证会话重定向异常
首次在CreateAccountScreen页面登录后,会话计时器结束时路由能正常跳转到LoginScreen;但第二次登录后,计时器结束却无法触发重定向。如何确保每次计时器结束时路由都能正确执行重定向?
相关代码
main.dart
import 'package:auto_rutas_prueba_auth/app/app.dart'; import 'package:auto_rutas_prueba_auth/bootstrap.dart'; void main() { bootstrap(App.new); }
get_it.dart
import 'package:get_it/get_it.dart'; GetIt _getIt = GetIt.instance; GetIt get getIt => _getIt;
bootstrap函数
Future<void> bootstrap(FutureOr<Widget> Function() builder) async { FlutterError.onError = (details) { log(details.exceptionAsString(), stackTrace: details.stack); }; Bloc.observer = const AppBlocObserver(); // Add cross-flavor configuration here WidgetsFlutterBinding.ensureInitialized(); getIt..registerSingleton<AuthProvider>(AuthProvider()) ..registerSingleton<AuthGuard>(AuthGuard()) ..registerSingleton<AppRouter>(AppRouter()); runApp(await builder()); }
app.dart
import 'package:auto_rutas_prueba_auth/get_it.dart'; import 'package:auto_rutas_prueba_auth/l10n/l10n.dart'; import 'package:flutter/material.dart'; class App extends StatelessWidget { App({super.key}); // final routerConfig = getIt<RouterConfig<UrlState>>(); final appRouter = getIt<AppRouter>(); final authProvider = getIt<AuthProvider>(); @override Widget build(BuildContext context) { return MaterialApp.router( theme: ThemeData( appBarTheme: AppBarTheme( backgroundColor: Theme.of(context).colorScheme.inversePrimary, ), useMaterial3: true, ), localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, // routerConfig: routerConfig, routerConfig: appRouter.config( reevaluateListenable: authProvider, ), ); } }
路由代码
import 'dart:async'; import 'package:auto_route/auto_route.dart'; import 'package:auto_rutas_prueba_auth/app/router/router.gr.dart'; import 'package:auto_rutas_prueba_auth/get_it.dart'; import 'package:flutter/cupertino.dart'; import 'package:logger/logger.dart'; @AutoRouterConfig() class AppRouter extends RootStackRouter { @override List<AutoRoute> get routes => [ AutoRoute( page: CreateAccountRoute.page, initial: true, ), AutoRoute( page: LoginRoute.page, path: '/login', ), AutoRoute( page: ProfileRoute.page, path: '/profile', guards: [getIt<AuthGuard>()], ), AutoRoute( page: MainRoute.page, path: '/home', guards: [getIt<AuthGuard>()], ), ]; } class AuthGuard extends AutoRouteGuard { @override void onNavigation(NavigationResolver resolver, StackRouter router) { if (getIt<AuthProvider>().isLoggedIn) { resolver.next(); } else { resolver.redirect(const LoginRoute(), replace: true); } } } class AuthProvider extends ChangeNotifier { bool _isLoggedIn = false; final Logger _logger = Logger(); Timer? _timer; bool get isLoggedIn => _isLoggedIn; void login() { _isLoggedIn = true; notifyListeners(); _startSession(); } void _startSession() { _timer?.cancel(); var secondsRemaining = 5; _logger.f('Sesión activa por $secondsRemaining segundos.'); _timer = Timer.periodic(const Duration(seconds: 1), (timer) { secondsRemaining--; _logger.i('$secondsRemaining segundos restantes.'); if (secondsRemaining <= 0) { logout(); timer.cancel(); } }); } void logout() { _isLoggedIn = false; notifyListeners(); } @override void dispose() { _timer?.cancel(); super.dispose(); } }
CreateAccountScreen
import 'package:auto_route/auto_route.dart'; import 'package:auto_rutas_prueba_auth/app/router/router.dart'; import 'package:auto_rutas_prueba_auth/app/router/router.gr.dart'; import 'package:auto_rutas_prueba_auth/get_it.dart'; import 'package:flutter/material.dart'; @RoutePage() class CreateAccountScreen extends StatelessWidget { const CreateAccountScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('CreateAccount Screen'), ), body: Center( child: ElevatedButton( onPressed: () { getIt<AuthProvider>().login(); context.router.replace( const MainRoute(), ); }, // onPressed: () => context.router.replace(const MainRoute()), child: const Text('Iniciar Sesión'), ), ), ); } }
LoginScreen
import 'package:auto_route/auto_route.dart'; import 'package:auto_rutas_prueba_auth/app/router/router.dart'; import 'package:auto_rutas_prueba_auth/app/router/router.gr.dart'; import 'package:auto_rutas_prueba_auth/get_it.dart'; import 'package:flutter/material.dart'; @RoutePage() class LoginScreen extends StatelessWidget { const LoginScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Login Screen'), ), body: Center( child: ElevatedButton( onPressed: () { getIt<AuthProvider>().login(); context.router.replace( const MainRoute(), ); }, child: const Text('Login'), ), ), ); } }
解决方案
原因分析
问题核心在于路由栈残留状态导致AutoRoute守卫重定向失效:首次登录后路由从初始页面切换到Main,logout时守卫能正确替换路由;但第二次登录从Login切换到Main后,路由栈仅保留Main,守卫的replace操作未彻底清理栈状态,导致重定向逻辑未被触发。
修复步骤
修改AuthGuard的重定向逻辑,清空路由栈
在AuthGuard的重定向代码中添加clearStack: true,确保每次重定向都彻底清空路由栈,避免残留状态干扰:class AuthGuard extends AutoRouteGuard { @override void onNavigation(NavigationResolver resolver, StackRouter router) { if (getIt<AuthProvider>().isLoggedIn) { resolver.next(); } else { // 清空路由栈并跳转到登录页 resolver.redirect(const LoginRoute(), replace: true, clearStack: true); } } }确保状态更新被正确监听
确认AuthProvider的logout方法中notifyListeners()已正确调用,保证AutoRoute的reevaluateListenable能感知到登录状态变化:void logout() { _isLoggedIn = false; notifyListeners(); // 触发AutoRoute重新评估守卫 }可选:主动触发路由跳转(兜底方案)
如果上述方法仍存在问题,可在logout方法中主动调用路由跳转,确保每次logout都直接导航到登录页:void logout() { _isLoggedIn = false; notifyListeners(); // 主动清空栈并跳转到登录页 getIt<AppRouter>().replace(const LoginRoute(), clearStack: true); }
验证效果
- 首次登录:从CreateAccount进入Main,计时结束后守卫检测到未登录,清空栈并跳转到Login。
- 第二次登录:从Login进入Main,计时结束后守卫同样触发重定向,确保每次会话超时都能正确跳转。
内容的提问来源于stack exchange,提问作者Samuel Alcántara
相关产品推荐
相关产品推荐

