Flutter应用递归调用3个函数致卡顿,求优化方案
你的问题核心在于在build()方法中直接调用getData()、getStatus()和getSearchText()——Flutter的build会在多种场景下频繁重建(比如setState触发、窗口尺寸变化、父组件更新等),这就导致你反复发起Firebase请求、多次执行过滤逻辑,还不断触发setState,最终造成应用卡顿甚至无限循环。
下面是具体的修复方案,一步步来:
1. 将初始化数据的函数移到initState中
getData()和getStatus()是获取初始数据的操作,只需要在组件初始化时执行一次,把它们移到initState里,避免重复触发:
@override void initState() { super.initState(); // 封装异步初始化逻辑,避免initState直接标记async _initializeData(); } Future<void> _initializeData() async { await getStatus(); await getData(); // 初始数据加载完后,如果有传入搜索文本,执行一次过滤 if (widget.searchText != null && widget.searchText.isNotEmpty) { getSearchText(); } }
2. 优化getStatus():避免循环中频繁调用setState
原来的getStatus()在循环里每次匹配都调用setState,会频繁触发组件重建。改成先找到匹配的sequenceName,再一次性更新状态:
String sequenceName; getStatus() async { final prefs = await SharedPreferences.getInstance(); if (!mounted) return; // 组件已销毁就停止操作,避免内存泄漏 setState(() { sequenceIndex = prefs.getInt("previousIndex"); }); QuerySnapshot snapshot = await Firestore.instance.collection('sequence').getDocuments(); // 先遍历找到匹配项,再统一更新状态 String foundSequenceName; for (var doc in snapshot.documents) { if (sequenceIndex == doc.data['index']) { foundSequenceName = doc.data['sequenceName']; break; // 找到后直接退出循环,减少不必要的遍历 } } if (!mounted) return; setState(() { sequenceName = foundSequenceName; }); }
3. 监听搜索文本变化:用didUpdateWidget替代build中调用
getSearchText()依赖于父组件传递的widget.searchText,只有当搜索文本变化时才需要执行过滤逻辑。重写didUpdateWidget来监听变化:
@override void didUpdateWidget(Clients1 oldWidget) { super.didUpdateWidget(oldWidget); // 只有当搜索文本真正变化时,才重新执行过滤 if (oldWidget.searchText != widget.searchText) { getSearchText(); } }
同时优化getSearchText()的逻辑,避免不必要的空值判断和重复操作:
void getSearchText() { if (widget.searchText == null || widget.searchText.isEmpty) { // 搜索文本为空时,直接恢复初始数据 setState(() { clientData = List.from(initialData); }); return; } final lowerCaseSearch = widget.searchText.toLowerCase(); List<DocumentSnapshot> tempList = []; for (var doc in initialData) { final name = "${doc["nameFs"] ?? ""}${doc["nameLs"] ?? ""}".toLowerCase(); final city = doc["city"]?.toLowerCase() ?? ""; final idOrder = doc["idOrder"]?.toLowerCase() ?? ""; if (name.contains(lowerCaseSearch) || idOrder.contains(lowerCaseSearch) || city.contains(lowerCaseSearch)) { tempList.add(doc); } } setState(() { clientData = tempList; }); }
4. 彻底移除build()中的函数调用
最后,把build()里的getSearchText()、getData()、getStatus()全部删掉,避免每次build都重复执行这些操作:
@override Widget build(BuildContext context) { // 这里不再调用那三个函数! if (!isGet) { return Center( child: CircularProgressIndicator( valueColor: AlwaysStoppedAnimation<Color>(appColor), ), ); } // 剩下的build逻辑保持不变... }
额外优化:用Firestore查询代替客户端过滤
如果你的sequenceName过滤逻辑可以放到Firestore查询里,就能减少从服务器获取的数据量,进一步提升性能:
getData() async { CollectionReference collectionReference = Firestore.instance.collection('clients'); Query query = collectionReference; // 如果sequenceName存在,直接在Firestore端过滤数据 if (sequenceName != null) { query = query.where('sequenceStatus', isEqualTo: sequenceName); } QuerySnapshot collectionSnapshot = await query.getDocuments(); clientData = collectionSnapshot.documents; initialData = collectionSnapshot.documents; if (!mounted) return; setState(() { isGet = true; }); }
这样调整后,你的应用只会在初始化时发起一次Firebase请求,搜索文本变化时才执行过滤逻辑,不会再因为build重复调用导致卡顿了。
内容的提问来源于stack exchange,提问作者Parth Bhanderi

