w461
w461

Reputation: 2698

streaming and transforming a Firestore document snapshots() stream

I am trying to get a document stream from Firestore, to extract one of its fields in my repository and to stream the field to my bloc. I use return orderBriefDoc.snapshots().map((snapshot) { for this.

However, upon first call, no internal map instruction becomes executed and instead I receive a type mismatch type '_MapStream<DocumentSnapshot, dynamic>' is not a subtype of type 'Stream<List<OrderBrief>>'. I do not understand why the return type of the .map() method does not depend on what I return within its return statement and why this internal code is not executed.

First of all, I used the repository function of Felix Angelov's Firebase authentication ant of the todo list as a blueprint:

Stream<User> get user {
    return _firebaseAuth.authStateChanges().map((firebaseUser) {
      return firebaseUser == null ? User.empty : firebaseUser.toUser;
    });
}

Stream<List<Todo>> todos() {
  return todoCollection.snapshots().map((snapshot) {
    return snapshot.documents
        .map((doc) => Todo.fromEntity(TodoEntity.fromSnapshot(doc)))
        .toList();
  });
}

My adaption looks like this

@override
Stream<List<OrderBrief>> orderBriefs() {
  if (orderBriefDoc == null)
    getOrderCollection();
  return orderBriefDoc.snapshots().map((snapshot) {
    final tmp = snapshot;
    print ("2");
    final tmp2 = snapshot.data()['orderBriefs'];
    print("3");
    return snapshot.data()['orderBriefs'].map((orderBrief) {
      final tmp=orderBrief;
      final tmp2 = OrderBriefEntity.fromMap(orderBrief);
      final tmp3 = OrderBrief.fromEntity(tmp2);
      return tmp3;
    }).toList();
  });
}

For some reason "2" and "3" are not printed upon first call, and due to the type mismatch the app execution fails. So in my function orderBriefs() I return a .map() of a snapshots() stream. The mapped value, so the single document snapshot is mapped again to extract the orderBriefs field. This field is transformed from an storage entity class OrderBriefEntity to my business logic class OrderBrief. The transformation result is the final return value. Hence I would expect the function orderBriefs() to return a list stream of this transformation result. However, a _MapStream<DocumentSnapshot, dynamic> is returned. Why?

PS: This refers to my question, but with a slightly different angle

Upvotes: 0

Views: 631

Answers (1)

w461
w461

Reputation: 2698

So, finally I found a method to stream a single document of Firestore. I finally had the idea to look up the documentation of Stream and found a working example there. Why it does not work with only the map method like Felix did it, no idea. That being said, I still follow his pattern to transform the snapshot to an "entity" and then further to the data structure used by the bloc and the ui.

I finally needed to flavors, stream a single field (nested array) of a document and stream a whole document

(1) Streaming a field of a document.

  @override
  Stream<List<OrderBrief>> orderBriefs() async* {
    if (orderBriefDoc == null)
      getOrderCollection();
    Stream<DocumentSnapshot> source = orderBriefDoc.snapshots();
    await for (var snapshot in source) {
      final List<OrderBrief> returnVal = snapshot.data()['orderBriefs'].map<OrderBrief>((orderBrief) {
        return OrderBrief.fromEntity(OrderBriefEntity.fromMap(orderBrief));
      }).toList();
      yield returnVal;
    }
  }

(2) Streaming a document with all of its fields

  @override
  Stream<Order> orderStream(String orderId) async* {
    Stream<DocumentSnapshot> source = orderBriefDoc.collection('orders').doc(orderId).snapshots();
    await for (var snapshot in source) {
      final Order returnVal = Order.fromEntity(OrderEntity.fromSnapshot(snapshot));
      yield returnVal;
    }
  }

How to neatly integrate this with a listener in the bloc, you will find in the Firestore ToDos example at bloclibrary

Upvotes: 2

Related Questions