1️⃣ 问题概述
在使用 GetX 和 Dio 时,常见场景:
- 用户已登录,但 token 过期
- 多个接口同时发起请求
- 后端返回 401
- 拦截器里跳转到登录页
问题出现了:
- 多次跳转到登录页
- LoginController 报
not found
本文分享如何优雅解决这个问题。
2️⃣ 问题复现流程(测试服手动让token过期,或切换IP地址)
- 登录测试服账号 → 正常进入首页
- 杀掉 App 进程
- 切换正式服配置,再次启动 App
- 进入首页请求接口返回 401
- 拦截器跳转登录页,报错:
scss
LoginController not found. You need to call "Get.put(LoginController())" or "Get.lazyPut(()=>LoginController())"
3️⃣ 问题原因分析
- 路由初始化时机 :App 启动
initialRoute可能不是登录页,而是首页 - 多个接口同时 401 :直接在拦截器里跳转,会导致重复
offAllNamed - GetView :页面会在 build 时自动
Get.find<LoginController>(),如果 binding 没执行就报错 - Controller 生命周期 :如果
lazyPut没加fenix,被释放后无法重建
核心:问题不是 GetX 绑定机制本身,而是拦截器并发触发 + 路由跳转时机导致 binding 没及时注册。
4️⃣ 解决方案
4.1 LoginBinding
scala
class LoginBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut<LoginController>(() => LoginController());
}
}
4.2 路由表配置
yaml
final pages = [
GetPage(
name: AppRoutes.login,
page: () => LoginPage(),
binding: LoginBinding(),
),
GetPage(
name: AppRoutes.main,
page: () => MainPage(),
binding: MainBinding(),
),
];
4.3 拦截器防抖跳转
ini
class AuthInterceptor extends Interceptor {
bool _isRedirectingToLogin = false;
@override
void onError(DioError err, ErrorInterceptorHandler handler) {
if (err.response?.statusCode == 401) {
if (!_isRedirectingToLogin) {
_isRedirectingToLogin = true;
Future.microtask(() {
Get.offAllNamed(AppRoutes.login);
_isRedirectingToLogin = false;
});
}
}
super.onError(err, handler);
}
}
5️⃣ 关键点解释
-
_isRedirectingToLogin- 防止多个接口同时返回 401,重复跳转
-
Future.microtask- 将跳转延迟到事件循环下一个微任务
- 避免 Flutter 当前 build / route 栈冲突
-
GetX Binding + GetView
- LoginController 会在跳转时自动注入
- 不需要
fenix: true,除非想全局保持
6️⃣ 总结
- 多接口并发 401 → 防抖 + 微任务 → 路由安全跳转
- 确保路由表绑定 LoginBinding → Controller 自动注入
- 这样既避免多次跳转,又解决
LoginController not found问题
适用于 Flutter + GetX + Dio 项目中常见的登录态管理场景