Flutter中GridView Builder内异步函数为何无限循环?
解决DynamicHeightGridView中异步函数触发无限重建的问题
问题根源
- 无限重建循环:
DynamicHeightGridView的builder每次触发都会调用listOfSensors,而listOfSensors里直接执行compareWinder,该函数最后调用setState触发Widget重建。重建后builder会再次调用listOfSensors,重复执行异步请求和setState,形成无限循环。 - 全局状态污染:
showWarning是全局变量,若后续列表项增多,会导致多个item的警告状态互相覆盖。 - Firebase请求逻辑冗余:
getLastDoc中遍历sensors的条件存在错误(sensors[i]['name'] == widget.list应为匹配当前item的名称),且limitToLast(1)后仍遍历所有值,没必要。
解决方案
1. 用FutureBuilder替代直接调用setState
将异步数据获取和UI渲染逻辑封装到FutureBuilder中,避免在build流程中直接触发setState。
修改listOfSensors函数:
Widget listOfSensors(int index) { String name = widget.list[index][0]; double setValue = double.parse(widget.list[index][2]); return FutureBuilder<double>( // 传入预初始化的future(后续优化点),避免每次build重新发起请求 future: getLastDoc(index, name), initialData: 0.0, builder: (context, snapshot) { // 根据异步返回值直接判断警告状态 bool showWarning = snapshot.hasData && snapshot.data! > setValue; return Container( decoration: BoxDecoration( color: const Color(0xFF393053), borderRadius: BorderRadius.circular(20)), child: Padding( padding: const EdgeInsets.all(10), child: GestureDetector( child: ListTile( leading: showWarning ? const Icon( Icons.warning, color: Colors.yellow, ) : null, title: Text( widget.list[index][0], style: TextStyle( color: Colors.white, fontSize: mobile ? 15 : 20), ), subtitle: Text( widget.list[index][1], style: TextStyle( color: Colors.white, fontSize: mobile ? 10 : 12), ), trailing: Icon( Icons.launch_rounded, color: Colors.white, size: mobile ? 20 : 30, )), ), )); }, ); }
2. 优化Firebase数据获取逻辑
修正遍历条件,简化数据解析:
Future<double> getLastDoc(int index, String name) async { String location = ""; double val = 0; // 修正匹配条件,对应当前列表项的名称 for (String i in sensors.keys) { if (sensors[i]['name'] == widget.list[index][0]) { location = i; break; } } final ref = FirebaseDatabase.instance.ref('items/$location/$name'); Query query = ref.limitToLast(1); final dataSnapshot = await query.once(); if (dataSnapshot.snapshot.exists) { Map data = dataSnapshot.snapshot.value as Map; // limitToLast(1)只会返回一条数据,直接取第一个值即可 var firstValue = data.values.first; EvapData json = EvapData.fromJson(firstValue); val = json.winder; print(val); } return val; }
3. 避免重复发起异步请求
在initState中预初始化所有item的请求Future,防止每次build都重新发起Firebase请求:
List<Future<double>>? _sensorFutures; @override void initState() { super.initState(); // 提前创建所有传感器的请求Future _sensorFutures = widget.list.map((item) { String name = item[0]; int index = widget.list.indexOf(item); return getLastDoc(index, name); }).toList(); }
然后在listOfSensors中替换future参数:
future: _sensorFutures![index],
4. (可选)实时监听Firebase数据变化
若需要实时感知数据库更新,将once()改为onValue监听,无需手动刷新:
Stream<double> listenToLastDoc(int index, String name) async* { String location = ""; for (String i in sensors.keys) { if (sensors[i]['name'] == widget.list[index][0]) { location = i; break; } } final ref = FirebaseDatabase.instance.ref('items/$location/$name'); await for (final event in ref.limitToLast(1).onValue) { double val = 0; if (event.snapshot.exists) { Map data = event.snapshot.value as Map; var firstValue = data.values.first; EvapData json = EvapData.fromJson(firstValue); val = json.winder; } yield val; } }
此时将FutureBuilder替换为StreamBuilder即可实现实时更新。
内容的提问来源于stack exchange,提问作者kati
相关产品推荐
相关产品推荐

