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

GetX控制器无法销毁:页面切换后仍运行,需仅当前页面控制器生效

问题描述

我写了一个funfetchUser函数,用来通过API检查用户登录状态(API不支持实时),代码如下:

void funfetchUser() async {
  print('get user');
  SharedPreferences prefs = await SharedPreferences.getInstance();
  var token = prefs.getString('token');
  try {
    var headers = {
      'Accept': 'application/json',
      'Authorization': 'Bearer $token',
    };
    var url = Uri.parse(Api.base + Api.user);
    https.Response response = await https.get(url, headers: headers);
    if (response.statusCode == 200) {
      var user = jsonDecode(response.body)['user'];
      var isLogin = token;
      print(isLogin);
      UserList.email = user['email'];
      UserList.userlongname = user['user_longname'];
      UserList.userphone = user['user_phone'];
      
      Future.delayed(Duration(seconds: 1), (() {
        if (isLogin == null) {
          Get.toNamed('/login');
        } else {
          print('stay');
        }
      }));
      
    } else {
      var login2 = jsonDecode(response.body);
      print(login2);
      // customAllertDialog("Sesi", 'Sesi Berakhir', 'error');
      print('logout');
      prefs.clear();
      Get.toNamed('/login');
    }
  } catch (e) {
    print(e);
    prefs.clear();
  }
}

之后我在所有页面的GetX控制器里集成了这个函数,代码示例如下:

@override
void onInit() {
  // TODO: implement onInit
  super.onInit();
  print('ini profile');
  funfetchUser();
}
@override
void onReady() {
  // TODO: implement onReady
  super.onReady();
  Timer.periodic(Duration(seconds: 30), (timer) { 
    print('cek user profile gan');
    funfetchUser();
  });
}
@override
void onClose() {
  // TODO: implement onClose
  super.onClose();
  Get.delete<ProfilController>();
}

现在遇到的问题是:切换页面后,之前页面的控制器还在持续运行(定时器一直在触发funfetchUser),我希望只有当前页面的控制器处于活跃状态,同时解决GetX控制器无法销毁的问题。

解决方案

1. 保存定时器实例,在onClose中手动取消

控制器无法销毁的核心原因是Timer.periodic未被取消,定时器持有控制器引用,导致GC无法回收。修改控制器代码:

Timer? _checkUserTimer; // 定义全局定时器变量

@override
void onInit() {
  super.onInit();
  print('ini profile');
  funfetchUser();
}

@override
void onReady() {
  super.onReady();
  // 初始化定时器并保存实例
  _checkUserTimer = Timer.periodic(Duration(seconds: 30), (timer) { 
    print('cek user profile gan');
    funfetchUser();
  });
}

@override
void onClose() {
  // 销毁控制器前必须取消定时器
  _checkUserTimer?.cancel();
  _checkUserTimer = null;
  // 无需手动调用Get.delete,绑定到页面的控制器会自动销毁
  super.onClose();
}

2. 确保控制器与页面绑定方式正确

GetX控制器要随页面自动销毁,必须通过页面绑定的方式初始化,而非全局初始化:

  • 在页面中使用Get.put(ProfilController())或Get.lazyPut(() => ProfilController())
  • 或者通过路由绑定初始化:Get.toNamed('/profile', binding: BindingsBuilder(() => Get.put(ProfilController())))

如果是在main函数或其他全局位置调用Get.put,控制器会一直处于活跃状态,无法随页面销毁。

3. 优化页面跳转逻辑,避免控制器销毁后执行

当前funfetchUser中的Future.delayed可能在控制器销毁后仍执行跳转,需添加状态判断:

Future.delayed(Duration(seconds: 1), () {
  // 检查控制器是否仍注册,避免无效跳转
  if (Get.isRegistered<ProfilController>() && Get.currentRoute != '/login') {
    if (isLogin == null) {
      Get.offNamed('/login'); // 用offNamed替换toNamed,避免登录页面叠加
    } else {
      print('stay');
    }
  }
});

4. 统一登录状态管理(可选优化)

无需在每个控制器都写定时检查逻辑,可将登录状态管理抽离到全局AuthController:

  • 创建AuthController,在其中统一管理定时器和登录状态
  • 监听GetX路由变化,仅在需要检查的页面启动定时器,切换页面时自动停止

内容的提问来源于stack exchange,提问作者StarsHat

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 15:10:12