Firebase not retrieving nested collection documents in Flutter project

142

Solution 1

contacts is a List not a sub-collection.

You can access it as below:

DocumentSnapshot<Map<String, dynamic>> snapshot = await FirebaseFirestore
    .instance
    .collection('users')
    .doc('yoTl9LhxxuaiIuo8jiMZjSXR0sU2')
    .get();

Map<String, dynamic>? user = snapshot.data();
print(user?['contacts']); // should print list of contacts

Solution 2

Looking at the screenshot of your firestore DB, contacts is not a sub-collection. But you are trying to read it as a sub collection. Contacts is either a List or Map.

Share:
142
kaci
Author by

kaci

Updated on January 04, 2023

Comments

  • kaci
    kaci over 1 year

    I've been working on a messaging app in Flutter and have been having some trouble with retrieving collection data from Firestore. I'm trying to retrieve a list of contacts for a user. This is my db structure: Firestore stucture

    I was following a slightly outdated tutorial that was using a stream transformer & mapping function to return a Stream of List like this:

    final contactsStream = userRef.snapshots().transform(
        StreamTransformer<DocumentSnapshot, List<Contact>>.fromHandlers(
            handleData: (documentSnapshot, sink) => mapDocumentToContact(
                usersRef, userRef, documentSnapshot, sink)));
    

    Mapping function:

    Future<void> mapDocumentToContact(
      CollectionReference userCollectionReference,
      DocumentReference contactReference,
      DocumentSnapshot documentSnapshot,
      Sink sink) async {
    List<String> contacts;
    
       final data = documentSnapshot.data() as DocumentSnapshot;
    
    if (data['contacts'] == null || data['conversations'] == null) {
      await contactReference.update({'contacts': []});
      contacts = [];
    } else {
      contacts = List.from(data['contacts']);
    }
    
    final contactList = <Contact>[];
    
    final Map? conversations = data['conversations'];
    
    for (final username in contacts) {
      final id = await getUserIdByUsername(username: username);
      final contactSnapshot = await userCollectionReference.doc(id).get();
      final Map<String, dynamic> contactSnapshotData =
          contactSnapshot.data() as Map<String, dynamic>;
    
      contactSnapshotData['conversationId'] = conversations![username];
      contactList.add(Contact.fromFirestore(contactSnapshot));
    }
    sink.add(contactList);
    

    This method, however throws a _StreamHandlerTransformer<DocumentSnapshot<Object?>, List<Contact>>' is not a subtype of type 'StreamTransformer<DocumentSnapshot<Map<String, dynamic>>, List<Contact>>' of 'streamTransformer’ error that I can't seem to get rid of no matter what combination of examples I've found online I try, so I've tried to just implement a simpler get() for the contacts (paths are normally set during runtime but I've got it here explicitly for debugging):

       QuerySnapshot<Map<String, dynamic>> snapshot = await fireStoreDb
        .collection('/users')
        .doc('yoTl9LhxxuaiIuo8jiMZjSXR0sU2')
        .collection('contacts')
        .get();
    
    var contactList = snapshot.docs
        .map((docSnapshot) => Contact.fromFirestore(docSnapshot))
        .toList();
    

    Mapping function:

      factory Contact.fromFirestore(DocumentSnapshot document) {
    final Map data = document.data() as Map<String, dynamic>;
    return Contact(
        document.id,
        data['username'], 
        data['conversationId']);
    

    This doesn't throw any errors but it also doesn't seem to fetch the contacts at all. I'm able to fetch the list of users using the same method, but as soon as I try a level down it doesn't return anything. I am, regrettably, at my wits' end, does anyone know what is happening here? Thanks.

  • kaci
    kaci about 2 years
    Hi, thanks for your reply; so I'd be retrieving the list of contacts as a document like this? var userSnapshot = await fireStoreDb .collection(FirebasePaths.usersPath) .doc('${userId}/contacts') .get(); Or am I misunderstanding?
  • kaci
    kaci about 2 years
    Hi, thanks for your reply! Managed to get it working this way. For clarification, it's a List and not a sub-collection due to contacts just containing an array of strings and not actual documents?
  • Peter Obiechina
    Peter Obiechina about 2 years
    Yes, a sub collection can contain documents, a list must be an array. If you want to create a contacts collection, tap Start collection just above add field and set the name to contacts.
  • kaci
    kaci about 2 years
    Great, thanks for clarifying!