如何在GetxController中正确销毁infinite_scroll_pagination的PagingController?
解决GetxController销毁后异步任务访问已释放PagingController的异常问题
问题场景
我在使用Getx时,通过GetxController的onClose方法释放资源:
@override void onClose() { pagingController.dispose(); super.onClose(); }
但遇到了异常:控制器内的异步方法_fetchPage负责获取分页数据并更新pagingController,如果在该方法执行完成前离开页面,异步任务不会自动终止,等数据返回后调用已销毁的pagingController时,会抛出:
Unhandled Exception: Exception: A PagingController was used after being disposed.
_fetchPage方法代码如下:
Future<void> _fetchPage(int pageKey) async { try { var skip = pageKey == 0 ? 0 : (10 * pageKey); var data = await fetchDataOfPage(skip, limit); final isLastPage = data.length < limit; if (isLastPage) { pagingController.appendLastPage(data); } else { final nextPageKey = pageKey + 1; pagingController.appendPage(data, nextPageKey); } } catch (error) { pagingController.error = error; } }
解决方案
核心是在控制器销毁时终止未完成的异步任务,或在任务完成后先检查资源状态再执行操作,以下是两种可行方案:
方案1:用CancelToken取消异步请求(推荐)
如果你的fetchDataOfPage是基于Dio实现的网络请求,可以借助CancelToken直接终止请求,避免无效资源消耗:
- 在控制器中声明CancelToken:
class YourController extends GetxController { final CancelToken _requestCancelToken = CancelToken(); late PagingController pagingController; // 其他初始化逻辑... }
- 修改
_fetchPage,传入CancelToken并增加状态检查:
Future<void> _fetchPage(int pageKey) async { try { var skip = pageKey == 0 ? 0 : (10 * pageKey); // 将CancelToken传入请求方法,内部用它控制请求生命周期 var data = await fetchDataOfPage(skip, limit, cancelToken: _requestCancelToken); final isLastPage = data.length < limit; // 检查控制器是否已销毁,是则直接返回 if (isClosed) return; if (isLastPage) { pagingController.appendLastPage(data); } else { final nextPageKey = pageKey + 1; pagingController.appendPage(data, nextPageKey); } } catch (error) { // 忽略请求取消的异常 if (error is DioException && error.type == DioExceptionType.cancel) return; // 确认控制器存活再设置错误状态 if (!isClosed) { pagingController.error = error; } } }
- 在
onClose中取消请求并释放资源:
@override void onClose() { // 取消所有未完成的请求 _requestCancelToken.cancel("控制器已销毁,终止请求"); pagingController.dispose(); super.onClose(); }
方案2:用Getx自带的isClosed状态检查
如果不需要终止异步任务,也可以在任务完成后先检查控制器状态,确认未销毁再操作pagingController:
修改_fetchPage方法:
Future<void> _fetchPage(int pageKey) async { try { var skip = pageKey == 0 ? 0 : (10 * pageKey); var data = await fetchDataOfPage(skip, limit); final isLastPage = data.length < limit; // 关键:先检查控制器是否已关闭 if (isClosed) return; if (isLastPage) { pagingController.appendLastPage(data); } else { final nextPageKey = pageKey + 1; pagingController.appendPage(data, nextPageKey); } } catch (error) { if (!isClosed) { pagingController.error = error; } } }
保留onClose的资源释放逻辑:
@override void onClose() { pagingController.dispose(); super.onClose(); }
额外说明
- 如果
fetchDataOfPage是自定义异步操作(非Dio请求),可以自己实现取消机制:比如声明bool _isDisposed变量,在onClose时设为true,在_fetchPage中检查该变量。 - 优先选择方案1,能直接终止无效请求,减少不必要的资源消耗。
内容的提问来源于stack exchange,提问作者blackkara
相关产品推荐
相关产品推荐

