Flutter可加载更多ListView实现搜索功能调用API时出现Widget已卸载异常
异常触发原因
这个报错是因为你的异步操作回调执行时,当前页面的State已经被销毁了:
你在_loadData、onSearchTextChanged、列表项点击事件里发起的API请求,还有_loadMoreData里的延时操作都是异步任务,这些任务的回调不会因为页面退出就停止执行。如果用户在请求还没返回的时候就关闭了当前页面,State已经被卸载,此时回调里再执行setState更新页面、或者用context执行路由跳转,就会触发这个异常。
修复方案
你可以按照以下步骤修改代码:
- 所有
setState执行前先判断mounted状态mounted是Flutter State自带的属性,为true表示当前State还挂载在页面上,只有符合这个条件才能执行setState。
举个修改_loadData的例子:
void _loadData() async { await StorageUtil.getInstance(); String cookie = StorageUtil.getString('sess'); String page = currentPage.toString(); if (cookie != null) { ApiService.getEstimateList(cookie, page, myAllestimate, search) .then((value) { // 增加mounted判断,已卸载就直接返回不执行后续逻辑 if (!mounted) return; if (value.data != null) { if (count < _totalRecords) { setState(() { invoicelist.addAll(value.data); print(value.data); _totalRecords = value.recordsTotal; }); print( 'List size is $count and TotalRecord - ${value.recordsTotal} , TotalFiltered - ${value.recordsFiltered}'); } } }); } }
所有异步回调里的setState都要按照这个逻辑增加mounted判断,包括onSearchTextChanged里的回调。
- 异步操作中使用
context前先判断mounted
比如列表项点击事件里的路由跳转,修改示例:
ApiService.viewInvoice(cookie, id, bmaster, 'estimate') .then((value) { // 增加mounted判断 if (!mounted) return; if (value != null) { var result = jsonDecode(value); if (result['status'].toString() == '1') { var decode = Uri.decodeFull(result['url']); var pdf_url = Uri.decodeFull(result['pdf_url']); pdfUrl = pdf_url.toString(); weburl = decode.toString(); Navigator.push( context, MaterialPageRoute( builder: (context) => WebViewContainer( weburl, 'Estimate', pdfUrl, mobile, id, custId), ), ); } } });
- 优化
_loadMoreData的异步逻辑
你当前的_loadMoreData没有等待_loadData执行完成就直接返回了true,会导致加载更多状态异常,修改为:
Future<bool> _loadMoreData() async { print('_loadMoreData()'); await Future.delayed(Duration(milliseconds: 1000)); currentPage += 1; await _loadData(); // 增加await等待数据加载完成 return true; }
- 可选优化:页面销毁时取消未完成的请求
如果要更彻底的规避问题,可以给API请求增加取消标记(比如Dio的CancelToken),重写dispose方法,在页面销毁时主动取消所有未完成的异步请求。
内容的提问来源于stack exchange,提问作者Vishali
相关产品推荐
相关产品推荐

