1️⃣ 问题概述
在使用 GetX 和 Dio 时,常见场景:
- 用户已登录,但 token 过期
- 多个接口同时发起请求
- 后端返回 401
- 拦截器里跳转到登录页
常见问题:
- 多次跳转到登录页
- LoginController 被重复初始化
- 页面报
LoginController not found
本文分享基于 拦截器防抖 + 当前路由判断 的解决方案。
2️⃣ 问题复现流程
- 登录测试服账号 → 正常进入首页
- 杀掉 App 进程
- 切换正式服配置,再次启动 App
- 进入首页请求接口返回 401
- 如果没有防抖,拦截器会多次跳转登录页,Binding 被重复执行
3️⃣ 问题原因分析
- 路由初始化时机:App 启动
initialRoute可能不是登录页 - 多个接口同时 401:直接在拦截器里跳转,会导致重复
offAllNamed - GetView :页面 build 时自动
Get.find<LoginController>(),如果 binding 没执行就报错 - Controller 生命周期:默认
lazyPut没加fenix或permanent,多次跳转会重复初始化
核心:问题不是 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️⃣ 关键点解释
-
防抖逻辑
_isRedirectingToLogin- 多接口同时返回 401,只触发一次跳转
-
Get.currentRoute判断- 避免在已经是登录页时重复跳转
- 防止 Binding 多次执行造成副作用
-
Future.microtask 延迟跳转
- 将跳转延迟到事件循环下一个微任务
- 避免 Flutter build/route 冲突
-
LoginController 生命周期
- 不使用 permanent 或 fenix,Controller 生命周期绑定页面
- 确保 build 时自动注入,避免
not found
6️⃣ 总结
- 多接口并发 401 → 防抖 + 当前路由判断 → 安全跳转登录页
- LoginBinding 可以重复执行,但 Controller 只在页面 build 时创建
- 结合微任务延迟跳转,避免路由栈冲突
- 适用于 Flutter + GetX + Dio 登录态管理场景
✅ 方法 B 保证多接口 401 只跳一次登录页,Binding 副作用可控,Controller 不重复初始化。