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

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;
  }
}

问题根源分析

  1. 依赖时序错误:getSearch依赖StreamBuilder生成的listNearby异步数据,但初始化getFuture时,listNearby还未被赋值,导致getSearch进入空值分支。
  2. 递归逻辑无效:getSearch空值分支中仅重新赋值getSearchFuture但未返回任何数据,导致Future直接标记为完成(connectionStatus.done)却没有有效数据,FutureBuilder因此一直显示加载状态。
  3. 全局变量依赖:依赖全局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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 22:44:57