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
相关产品推荐
相关产品推荐

