如何在 Flutter 中嵌套 StreamBuilder?

Posted

技术标签:

【中文标题】如何在 Flutter 中嵌套 StreamBuilder?【英文标题】:How can you nest StreamBuilders in Flutter? 【发布时间】:2019-12-10 04:42:31 【问题描述】:

我需要将 2 个 Streams 组合起来以构建一个小部件,但与我见过的其他问题不同,我需要嵌套我的流。

我有一个从 Firestore 获取文档集合的流,还有一个依赖于第一个数据的流来获取文档的子集合。我想将它们组合成一个流,但它们需要嵌套,因为每个文档都有自己的文档子集。

Stream 1(从 FireStore 获取习惯集合):

Stream<List> getHabits()
  final Stream<QuerySnapshot> documents = Firestore.instance
    .collection("users")
    .document('VtL1sxOoCOdJaOTT87IbMRwBe282')
    .collection("habits")
    .snapshots();

  Stream<List> data = documents.map((doc) 
    List data;
    final documents = doc.documents;
    ///Maybe this would work to get history of each doc? 
    for(int i = 0; i < documents.length; i++)
      ///not sure what to do
      getHistory(documents[i].documentID, DateTime.utc(2019,7,7), DateTime.now());
    

    data = documents.map((documentSnapshot) => documentSnapshot).toList();

    return data;
  );

  return data;

Stream 2(在Stream 1中调用,以DocumentID为参数,获取文档的子集合):

Stream<List> getHistory(String id, DateTime start, DateTime end) async* 
  await for (QuerySnapshot querySnapshot in Firestore.instance
    .collection("users")
    .document('VtL1sxOoCOdJaOTT87IbMRwBe282')
    .collection("habits")
    .document(id)
    .collection("history")
    .where('day', isGreaterThanOrEqualTo: start)
    .where('day', isLessThanOrEqualTo: end)
    .snapshots()) 

      List history;
      final documents = querySnapshot.documents;

      history = documents.map((documentSnapshot) => documentSnapshot).toList();

      yield history;
    

任何关于如何将这些流以嵌套格式组合成一个流以在颤振中与StreamBuilder 一起使用的任何帮助将不胜感激!'

编辑 我不确定我的工作方向是否正确,但我已尝试从 spenster 实施解决方案,这是我目前除了上述功能之外的功能。

StreamBuilder<List>(
  stream: getHabits(),
  initialData: [],
  builder: (context, snapshot) 
    List<UserHabit> habits = [];
    List<Widget> test = List.generate(snapshot.data.length, (index)
      List<History> history = [];
      DocumentSnapshot doc = snapshot.data[index];
      return StreamBuilder(
        stream: getHistory(doc.documentID, DateTime.utc(2019,7,7), DateTime.now()),
        builder: (context, snapshot) 
          if (snapshot.hasError)
            return new Text('Error: $snapshot.error');
          switch (snapshot.connectionState) 
            case ConnectionState.waiting: return new Text('Loading...');
            default:
              if(!snapshot.data.isEmpty) //history collection exists
                for(int i = 0; i < snapshot.data.length; i++)
                  //add to history
                  history.add(History(
                    day: snapshot.data[i]['day'].toDate(), 
                    dateCompleted: snapshot.data[i]['dateCompleted'].toDate(), 
                    morning: snapshot.data[i]['morning'],
                    afternoon: snapshot.data[i]['afternoon'],
                    evening: snapshot.data[i]['evening'],
                    anytime: snapshot.data[i]['anytime'],
                  ));
                
              
              habits.add(UserHabit(
                name: doc['habit'],
                color: doc['color'],
                icon: doc['icon'],
                repeat: doc['repeat'],
                daily: doc['daily'],
                weekly: doc['weekly'],
                monthly: doc['monthly'],
                time: doc['time'],
                history: history,
              ));
              print(habits); //returns each iteration of assembling the list
              return Text("i dont want to return anything");
          
        ,
      );
      
    );
    print(habits); //returns empty list before anything is added
    return Column(
      children: test,
    );

  ,
),

UserHabits 和 History 的类可以共享,但它们只是分配类型并允许轻松访问的基本类。

【问题讨论】:

【参考方案1】:

我使用嵌套的StreamBuilders 做了类似的事情。根据您希望Widgets 的组织方式,您可以在外部StreamBuilder 内创建流。根据您澄清的 cmets,这是一种可能性:

@override
Widget build(BuildContext context) 

  var habits = Firestore.instance
    .collection("users")
    .document('VtL1sxOoCOdJaOTT87IbMRwBe282')
    .collection("habits")
    .snapshots();

  return StreamBuilder<QuerySnapshot>(
    stream: habits,
    builder: (context, snapshot) 

      if (!snapshot.hasData)
        return Text("Loading habits...");

      return ListView(children: snapshot.data.documents.map((document) 

        var query = Firestore.instance
          .collection("users")
          .document('VtL1sxOoCOdJaOTT87IbMRwBe282')
          .collection("habits")
          .document(document.documentID)
          .collection("history")
          .where('day', isGreaterThanOrEqualTo: start)
          .where('day', isLessThanOrEqualTo: end)
          .snapshots();

        return StreamBuilder<QuerySnapshot>(
          stream: query,
          builder: (context, snapshot) 

            if (!snapshot.hasData) return Text("Loading...");

            // right here is where you need to put the widget that you
            // want to create for the history entries in snapshot.data...
            return Container();
          ,
        );
      ).toList());
    ,
  );

【讨论】:

谢谢!我会试一试,如果可行,我会将您的答案标记为正确!感谢您花时间回答问题! 只是出于好奇,有什么办法我不能在getHistoryStreamBuilder 中不返回任何内容吗?我需要使用history 数据列表来查看事情是否已完成,但我只在getHabits() 中为每个项目构建一个小部件 我也收到以下错误The element type 'Set&lt;StreamBuilder&lt;List&gt;&gt;' can't be assigned to the list type 'Widget'. 如果您只想要历史收藏中的一个文档,那么您可以使用 Firestore 来选择它(比如在查询中使用 .limit(1)),然后只构建一个小部件。这也应该顺便消除您也看到的其他错误。我会看看我是否可以更新答案... 我想我设法让这种方法起作用,但我觉得应该有一种方法可以嵌套流而不需要嵌套流构建器。或者firebase应该更容易访问子文档哈哈。非常感谢您抽出宝贵的时间,我会将其标记为正确的!大约一周以来一直在解决这个难题。【参考方案2】:

尝试将您的流与Observable.zip2(stream1,stream2,zipper)Observable.combineLatest2(streamA, streamB, combiner) 之类的内容合并。

欲了解更多信息,请查看post

【讨论】:

我喜欢这个方向,但第二个流取决于第一个流中项目的documentID。第二个流对于每个项目都与第一个不同。我需要它们以某种方式嵌套而不是压缩

以上是关于如何在 Flutter 中嵌套 StreamBuilder?的主要内容,如果未能解决你的问题,请参考以下文章

在 Flutter 的嵌套 Navigator 结构中,如何获取特定的 Navigator?

如何在flutter中使用API​​调用嵌套的json数据?

如何在 Flutter 中使用 Firestone 的 streambuilder 获取嵌套文档?

如何在 Flutter 中获取嵌套 firebase 数据的所有子数据?

如何在flutter的sqlite数据库中保存嵌套的复杂JSON数据

如何在Dart / flutter中映射和显示嵌套的JSON?