You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter应用递归调用3个函数致卡顿,求优化方案

解决Flutter主Widget重复调用Firebase函数导致的卡顿问题

你的问题核心在于在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 09:02:02