Flutter + GetX:Dio 多接口 401 拦截后跳登录,避免重复跳转和 Controller 找不到问题

1,437 阅读2分钟

1️⃣ 问题概述

在使用 GetXDio 时,常见场景:

  • 用户已登录,但 token 过期
  • 多个接口同时发起请求
  • 后端返回 401
  • 拦截器里跳转到登录页

常见问题:

  • 多次跳转到登录页
  • LoginController 被重复初始化
  • 页面报 LoginController not found

本文分享基于 拦截器防抖 + 当前路由判断 的解决方案。


2️⃣ 问题复现流程

  1. 登录测试服账号 → 正常进入首页
  2. 杀掉 App 进程
  3. 切换正式服配置,再次启动 App
  4. 进入首页请求接口返回 401
  5. 如果没有防抖,拦截器会多次跳转登录页,Binding 被重复执行

3️⃣ 问题原因分析

  • 路由初始化时机:App 启动 initialRoute 可能不是登录页
  • 多个接口同时 401:直接在拦截器里跳转,会导致重复 offAllNamed
  • GetView :页面 build 时自动 Get.find<LoginController>(),如果 binding 没执行就报错
  • Controller 生命周期:默认 lazyPut 没加 fenixpermanent,多次跳转会重复初始化

核心:问题不是 GetX 本身,而是 拦截器并发触发 + 路由跳转时机 导致 Controller 初始化多次。


4️⃣ 解决方案

4.1 LoginBinding

class LoginBinding extends Bindings {
  @override
  void dependencies() {
    // 页面跳转时注入 Controller
    Get.lazyPut<LoginController>(() => LoginController());
    print("LoginBinding executed");
  }
}

注意:Binding 会在每次进入页面时调用,但 Controller 只会在页面真正 build 时创建实例。


4.2 路由表配置

final pages = [
  GetPage(
    name: AppRoutes.login,
    page: () => LoginPage(),
    binding: LoginBinding(),
  ),
  GetPage(
    name: AppRoutes.main,
    page: () => MainPage(),
    binding: MainBinding(),
  ),
];

4.3 Dio 拦截器防抖跳转

class AuthInterceptor extends Interceptor {
  bool _isRedirectingToLogin = false;
​
  @override
  void onError(DioError err, ErrorInterceptorHandler handler) {
    if (err.response?.statusCode == 401) {
      // 防止重复跳转,并且确保当前不是登录页
      if (!_isRedirectingToLogin && Get.currentRoute != AppRoutes.login) {
        _isRedirectingToLogin = true;
​
        // 延迟到微任务,避免 build/route 冲突
        Future.microtask(() {
          Get.offAllNamed(AppRoutes.login);
          _isRedirectingToLogin = false;
        });
      }
    }
    super.onError(err, handler);
  }
}

5️⃣ 关键点解释

  1. 防抖逻辑 _isRedirectingToLogin

    • 多接口同时返回 401,只触发一次跳转
  2. Get.currentRoute 判断

    • 避免在已经是登录页时重复跳转
    • 防止 Binding 多次执行造成副作用
  3. Future.microtask 延迟跳转

    • 将跳转延迟到事件循环下一个微任务
    • 避免 Flutter build/route 冲突
  4. LoginController 生命周期

    • 不使用 permanent 或 fenix,Controller 生命周期绑定页面
    • 确保 build 时自动注入,避免 not found

6️⃣ 总结

  • 多接口并发 401 → 防抖 + 当前路由判断 → 安全跳转登录页
  • LoginBinding 可以重复执行,但 Controller 只在页面 build 时创建
  • 结合微任务延迟跳转,避免路由栈冲突
  • 适用于 Flutter + GetX + Dio 登录态管理场景

✅ 方法 B 保证多接口 401 只跳一次登录页,Binding 副作用可控,Controller 不重复初始化。