You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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操作未彻底清理栈状态,导致重定向逻辑未被触发。

修复步骤

  1. 修改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);
        }
      }
    }
    
  2. 确保状态更新被正确监听
    确认AuthProvider的logout方法中notifyListeners()已正确调用,保证AutoRoute的reevaluateListenable能感知到登录状态变化:

    void logout() {
      _isLoggedIn = false;
      notifyListeners(); // 触发AutoRoute重新评估守卫
    }
    
  3. 可选:主动触发路由跳转(兜底方案)
    如果上述方法仍存在问题,可在logout方法中主动调用路由跳转,确保每次logout都直接导航到登录页:

    void logout() {
      _isLoggedIn = false;
      notifyListeners();
      // 主动清空栈并跳转到登录页
      getIt<AppRouter>().replace(const LoginRoute(), clearStack: true);
    }
    

验证效果

  • 首次登录:从CreateAccount进入Main,计时结束后守卫检测到未登录,清空栈并跳转到Login。
  • 第二次登录:从Login进入Main,计时结束后守卫同样触发重定向,确保每次会话超时都能正确跳转。

内容的提问来源于stack exchange,提问作者Samuel Alcántara

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 01:49:52