Flutter中FutureBuilder依赖StreamBuilder数据始终加载问题求助
问题:FutureBuilder始终处于加载状态,仅热重载后正常工作
我的FutureBuilder一直卡在“Loading...”状态,只有热重载或者修改代码重新加载后才能正常显示数据。目前用1000ms自动刷新的临时方案凑合用,但代码质量太差。
StreamBuilder用来根据用户位置生成卡片列表,其中listNearby列表是在StreamBuilder内部生成的,用来获取8个最近位置的快照——这是我从geoflutterfire2响应里拿到这些数据的唯一方式。
代码片段
Widget build方法
Widget build(BuildContext context) { Stream<List<DocumentSnapshot>> stream = geo.collection(collectionRef: ref).within(center: center, radius: radius, field: field); return Scaffold( body: StreamBuilder<List<DocumentSnapshot>>( stream: stream, builder: (context, AsyncSnapshot<List<DocumentSnapshot>> snapshot) { if (snapshot.data != null) { List<DocumentSnapshot<Object?>>? streamList = snapshot.data?.toList(); for (var i = 0; i < 8; i++) { var tester = streamList![i].id; courtTest.add(tester); listNearby = List<String>.from(courtTest); Future.delayed(Duration.zero, () async { listNearby = List<String>.from(courtTest); }); } return SizedBox( height: 140, child: ListView.separated( scrollDirection: Axis.horizontal, itemCount: 8, separatorBuilder: (context, index) { return const SizedBox(width: 15); }, itemBuilder: (context, index) { ///additional itembuilder stuff return ///some user interface; }), ); }), FutureBuilder( future: getFuture, builder: (_, snapshot) { if (snapshot.data != null) { return Container( height: 140, child: ListView.separated( scrollDirection: Axis.horizontal, itemCount: snapshot.data.length, separatorBuilder: (context, index) { return const SizedBox(width: 15); }, itemBuilder: (context, index) { return buildUpcomingCard( ///card data ); }), ); } else { Future.delayed(const Duration(milliseconds: 1000), () { setState(() { getFuture = getSearch(); }); }); return const Text('Loading...'); } }, ),
getSearch异步函数
getSearch() async { var listNearbyNew = []; print('do we go here?'); if (listNearby.isEmpty) { await Future.delayed(const Duration(milliseconds: 10), () { print('how about here?'); getSearchFuture = getSearch(); }); } else { print('did we make it to the else?'); var firestore = FirebaseFirestore.instance; for (var i = 0; i < 8; i++) { DocumentSnapshot gG = await firestore.collection('xxxx').doc(listNearby[i]).get(); listTest.add(gG.data().toString()); try { for (var x = 0; x < 3; x++) { final incremDt = _now.add(Duration(days: x)); final incremDtFrmtd = DateFormat('MM-dd-yyyy').format(incremDt); var regexp = RegExp(r"(?<=host-).+?(?=:)"); Iterable<Match> matches = regexp.allMatches(gG.get(incremDtFrmtd).toString(), 0); if (matches.isNotEmpty) { for (final Match h in matches) { String match2 = h[0]!; //hostEmail var info = gG.get(incremDtFrmtd)['xxx-$match2']; // this returns the info var xxx = info['xxx']; List xx = info['xxx']; List xx = info['xx']; List xxx = info['xxx']; listNearbyNew.add({ ///add list items }); } } } } catch (e) { // print(e); } } listNearbyNew.sort((a, b) { int dateComp = a['date'].compareTo!(b['date']); if (dateComp == 0) { return parseTime(a['time'][0]).compareTo(parseTime((b['time'][0]))); } return dateComp; }); print('done'); return listNearbyNew; } }
问题根源分析
- 依赖时序错误:
getSearch依赖StreamBuilder生成的listNearby异步数据,但初始化getFuture时,listNearby还未被赋值,导致getSearch进入空值分支。 - 递归逻辑无效:
getSearch空值分支中仅重新赋值getSearchFuture但未返回任何数据,导致Future直接标记为完成(connectionStatus.done)却没有有效数据,FutureBuilder因此一直显示加载状态。 - 全局变量依赖:依赖全局
listNearby传递数据,状态同步不可靠,容易出现数据未就绪的情况。
修复方案
1. 重构getSearch,移除无效递归并改为参数传递
让getSearch仅负责数据处理,不处理等待逻辑,通过参数接收listNearby避免全局依赖:
Future<List<dynamic>> getSearch(List<String> listNearby) async { final listNearbyNew = []; if (listNearby.isEmpty) return []; final firestore = FirebaseFirestore.instance; for (var i = 0; i < listNearby.length; i++) { final gG = await firestore.collection('xxxx').doc(listNearby[i]).get(); try { for (var x = 0; x < 3; x++) { final incremDt = _now.add(Duration(days: x)); final incremDtFrmtd = DateFormat('MM-dd-yyyy').format(incremDt); final regexp = RegExp(r"(?<=host-).+?(?=:)"); final matches = regexp.allMatches(gG.get(incremDtFrmtd).toString(), 0); if (matches.isNotEmpty) { for (final h in matches) { final match2 = h[0]!; final info = gG.get(incremDtFrmtd)['xxx-$match2']; listNearbyNew.add({ // 填充数据字段 }); } } } } catch (e) { // 异常处理,例如打印日志 } } listNearbyNew.sort((a, b) { final dateComp = a['date'].compareTo(b['date']); if (dateComp == 0) { return parseTime(a['time'][0]).compareTo(parseTime(b['time'][0])); } return dateComp; }); return listNearbyNew; }
2. 嵌套FutureBuilder到StreamBuilder内部
确保只有当StreamBuilder拿到有效数据并生成listNearby后,才初始化getSearch,保证数据就绪:
Widget build(BuildContext context) { final stream = geo.collection(collectionRef: ref).within(center: center, radius: radius, field: field); return Scaffold( body: StreamBuilder<List<DocumentSnapshot>>( stream: stream, builder: (context, AsyncSnapshot<List<DocumentSnapshot>> snapshot) { if (!snapshot.hasData || snapshot.data!.isEmpty) { return const Text('加载附近位置中...'); } // 生成listNearby,直接取前8个 final listNearby = snapshot.data!.take(8).map((doc) => doc.id).toList(); return Column( children: [ // 原有的附近位置列表 SizedBox( height: 140, child: ListView.separated( scrollDirection: Axis.horizontal, itemCount: listNearby.length, separatorBuilder: (_, __) => const SizedBox(width: 15), itemBuilder: (context, index) { // 你的UI组件 return const Placeholder(); }, ), ), // 依赖listNearby的FutureBuilder FutureBuilder<List<dynamic>>( future: getSearch(listNearby), builder: (_, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Text('加载推荐数据中...'); } if (snapshot.hasError || snapshot.data!.isEmpty) { return const Text('暂无推荐数据'); } return Container( height: 140, child: ListView.separated( scrollDirection: Axis.horizontal, itemCount: snapshot.data!.length, separatorBuilder: (_, __) => const SizedBox(width: 15), itemBuilder: (context, index) { return buildUpcomingCard( // 传入snapshot.data![index]的相关数据 ); }, ), ); }, ), ], ); }, ), ); }
3. 移除临时刷新逻辑和全局变量
删掉原来的全局listNearby、getFuture以及FutureBuilder里的1000ms自动刷新代码,完全依赖StreamBuilder的状态驱动后续流程。
内容的提问来源于stack exchange,提问作者Pierre Louet
相关产品推荐
相关产品推荐

