简体   繁体   English

如何在 Flutter 中嵌套 StreamBuilders?

[英]How can you nest StreamBuilders in Flutter?

I have 2 Streams that I need to combine to build a widget, but unlike other questions I have seen I need to nest my streams.我有 2 个流,我需要将它们组合起来以构建一个小部件,但与我见过的其他问题不同,我需要嵌套我的流。

I have a stream that gets a collection of documents from Firestore, and a stream that depends on data from the first to get a subcollection of documents.我有一个从 Firestore 获取文档集合的流,以及一个依赖于从第一个获取文档子集合的数据的流。 I would like to combine these into one stream, but they need to be nested since each document has its own subcollection of documents.我想将它们组合成一个流,但它们需要嵌套,因为每个文档都有自己的文档子集合。

Stream 1 (Gets a collection of habits from 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 (Called in Stream 1, Takes DocumentID as a parameter, gets sub-collection of documents):流2(在流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;
    }
}

Any help on how I can combine these streams in a nested format into one stream to be used with StreamBuilder in flutter would be appreciated!'任何关于如何将这些流以嵌套格式组合成一个流以与StreamBuilder在 flutter 中一起使用的任何帮助将不胜感激!

EDIT I am not sure if I am working in the right direction or not but I have tried to implement the solution from spenster and this is what I have at the moment in addition to the functions above.编辑我不确定我是否朝着正确的方向工作,但我已经尝试从 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,
    );

  },
),

The Class for UserHabits and History can be shared, but they are just basic classes that assign types and allow easy access. UserHabits 和 History 的类可以共享,但它们只是分配类型并允许轻松访问的基本类。

I have done something similar simply using nested StreamBuilders .我只是使用嵌套的StreamBuilders做了类似的StreamBuilders Depending on how you want your Widget s organized, you can create streams within the outer StreamBuilder .根据您希望Widget的组织方式,您可以在外部StreamBuilder创建流。 Based on your clarifying comments, this is one possibility:根据您的澄清意见,这是一种可能性:

@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());
    },
  );
}

Try merging your streams with something like Observable.zip2(stream1,stream2,zipper) or Observable.combineLatest2(streamA, streamB, combiner) .尝试将您的流与Observable.zip2(stream1,stream2,zipper)Observable.combineLatest2(streamA, streamB, combiner)

For more info, check this post有关更多信息,请查看此帖子

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM