Flutter搜索产品时ListView需二次提交才填充数据的异常问题
问题根因定位
首次搜索完成后列表不渲染、需要二次提交才能显示数据,是两个时序逻辑错误共同导致的:
setState()调用时机完全错位:当前代码在触发异步API请求后,不等请求返回、不等XML解析完成、不等新数据写入products列表,就立刻执行了空逻辑的setState()。此时products还是初始的空值,重绘后界面自然没有内容。等异步请求实际完成、把解析好的产品数据追加到列表时,没有任何逻辑触发界面重绘,你完全看不到刚拉到的结果。直到第二次提交搜索时,onSubmitted回调里的setState()才触发重绘,而此时第一次请求早就把数据写入了products,列表才会正常展示。- 数据源无重置逻辑:每次搜索都直接往同一个
products列表追加数据,重复搜索会不断累积历史结果,后续很容易出现列表混入旧数据的问题。
修复方案
按照以下步骤改造代码即可解决渲染时序问题:
- 将数据清空、
setState()重绘的逻辑移到异步请求完成、所有数据写入列表的阶段 - 移除onSubmitted回调里无意义的空
setState()调用 - 加上页面挂载状态判断,避免异步回调触发未挂载组件的重绘报错
改造后的代码示例
首先修改产品拉取方法:
Future<void> _fetchClientProducts(String client, String product) async { final String url = "http://192.168.25.48/lomacs/xml/integration?query=getproducts&client=$client&product_id=$product"; try { // 新搜索开始前清空旧的列表数据,避免历史结果累积 products.clear(); final response = await http.get(url); final xmlDocument = xml.XmlDocument.parse(response.body); for (var xmlElement in xmlDocument.findAllElements('product')) { products.add( Product( productId: xmlElement.findElements('product_id').single.text, scanId: xmlElement.findElements('scan_id').single.text, productTitle: xmlElement.findElements('prod_title').single.text, qtyAvailable: xmlElement.findElements('qty_availability').single.text, productImage: xmlElement.findElements('product_image').single.text, ), ); } // 所有数据更新完成后,确认页面仍在挂载状态,再触发重绘 if (mounted) { setState(() {}); } } catch (e) { print(e); } }
然后修改TextField的提交回调,移除无效的setState:
TextField( decoration: InputDecoration( prefixIcon: Icon(Icons.search), hintText: "Search for Product"), onSubmitted: (String str) { if (str.length == 0) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('you must enter a product to search'), ), ); return; } print("search for product: $str"); // 直接触发拉取,拉取完成后会自动触发重绘 _fetchClientProducts('CLIENTCODE', str); }, ),
优化建议
- 可以新增一个
isLoading状态变量,在请求发起时设为true、请求结束后设为false,在列表区域显示加载动画,避免用户等待时无反馈 - 当前硬编码的本地IP接口地址,上线前需要替换为正式生产环境的域名
- 后续可以考虑给products列表加长度判断,当搜索无结果时显示空状态提示,提升交互体验
内容的提问来源于stack exchange,提问作者iStealth
相关产品推荐
相关产品推荐

