Flutter Firestore - для получения снимков документов, принадлежащих только определенному набору идентификаторов, и отображения этих данных на экране.

Я передаю свой список конструктора идентификаторов документов Firestore, и я хочу получить и показать данные, принадлежащие этим идентификаторам, только в моей коллекции LinksData.

Итак, в основном я хочу получить моментальный снимок документа всех документов, принадлежащих идентификаторам, которые находятся в другом моем списке (который я передаю в конструкторе).

Я мог придумать два метода:

  1. Используя StreamBuilder, но, насколько мне известно, с его помощью я получу либо все снимки документов в моей коллекции, либо только один.
  2. Используя Future Builder, я сделал следующую функцию, но когда я ее использую, она показывает следующую ошибку
[ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: Bad state: field does not exist within the DocumentSnapshotPlatform

Как я могу это сделать? Я не хочу получать все документы с помощью построителя потоков, потому что это повлияет на мои ежедневные чтения.

Вот код того, как я пытался использовать FutureBuilder.

FutureBuilder<List<LinkCards>>(
                future: generateList(widget.linksDataIds),
                builder: (context, snapshot) {
                  if (snapshot.hasError) {
                    return Center(child: Text('Oops! Some Error Occurred!'));
                  } else {
                    if (!snapshot.hasData) {
                      return Center(child: CircularProgressIndicator());
                    }
                    return GridView.builder(
                        gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
                          maxCrossAxisExtent: 220,
                          mainAxisSpacing: 12.0,
                        ),
                        itemCount: snapshot.data!.length,
                        itemBuilder: (context, index) {
                          return snapshot.data![index];
                        });
                  }
                },
              )

Вот моя будущая функция

  Future<List<LinkCards>> generateList(List<dynamic> listLinksDataId) async {
    FirebaseFirestore fireStore = FirebaseFirestore.instance;
    List<LinkCards> listCards = [];

    listLinksDataId.forEach((linkDataId) async {
      print(linkDataId);

      await fireStore
          .collection('LinksData')
          .doc(linkDataId)
          .get()
          .then((value) => {
                listCards.add(LinkCards(
                  link: value.get('link'),
                  linkTitle: value.get('title'),
                  linkImage: value.get('image'),
                  relatedCategories: value.get('categories'),
                  platform: value.get('platform'),
                ))
              });
    });
    return listCards;
  }

Вот изображение структуры моей базы данных: введите здесь описание изображения


person Aamir Zaidi    schedule 31.05.2021    source источник


Ответы (3)


Так в идеале должна выглядеть ваша функция. Попробуйте, и, надеюсь, это сработает.

Future<List<LinkCards>> generateList(List<dynamic> listLinksDataId) async {
    FirebaseFirestore fireStore = FirebaseFirestore.instance;
    List<LinkCards> listCards = [];
    for(int i=0; i<listLinksDataId.length;i++){

       final firestoreResult = await fireStore
          .collection('LinksData')
          .doc(linkDataId)
          .get();
       listCards.add(
        link: firestoreResult.data()["link"],
        linkTitle: firestoreResult.data()["title"],
        linkImage: firestoreResult.data()["image"],
        relatedCategories: firestoreResult.data()["categories"],
        platform: firestoreResult.data()["platform"],
       );

    }
    return listCards;
  }

Я обычно придерживаюсь этого подхода и, надеюсь, будет полезен и для вас.

person Aashar Wahla    schedule 31.05.2021
comment
При этом управление переходит в snapshot.hasError, и при дальнейшей печати firestoreResult.data () [link] он по-прежнему дает null., Даже если он есть в базе данных. - person Aamir Zaidi; 31.05.2021
comment
Ошибка в том, что эта функция не знает, что такое linkDataId. В исходном вопросе это было из цикла forEach, но в этом с обычным циклом for linkDataId вообще не существует. - person Loren.A; 01.06.2021

Это должно подвести вас довольно близко. Это упрощенный пример, который я могу воспроизвести, который просто использует список строк.

Future<List<String>> generateList(List<String> listLinksDataId) async {
    List<String> listCards = [];
    final collection = FirebaseFirestore.instance.collection('LinksData');
    for (String docId in listLinksDataId) {
      final snapshot = await collection.doc(docId).get(); 

     // snapshot is the document snapshot whose id 
     // matches the index in the list you're passing in

      final linksData = snapshot['linksData']; // list of ids in linksData field

      for (var i = 0; i < linksData.length; i++) { // looping through list
        final field = linksData[i];
        listCards.add(field);
      }
    }
    return listCards;
  }

Это, по крайней мере, вернет все идентификаторы в поле linksData только в тех документах, которые соответствуют индексу переданного вами списка.

Вы должны иметь возможность настроить быструю тестовую страницу и FutureBuilder<ListString>> и передать этот метод как Future.

Верните ListView, который просто отображает текстовые виджеты, и вы увидите все идентификаторы, которые хотите.

Expanded(
   child: ListView.builder(
      itemCount: snapshot.data!.length,
      itemBuilder: (context, index) =>
         Text(snapshot.data![index])),
   );

Затем вы можете изменить оттуда, чтобы полностью заполнить ваш LinkCard.

person Loren.A    schedule 31.05.2021

Спасибо за ответы, но я нашел решение, немного изменив свою базу данных. В коллекции LinksData я помещаю поле (массив типов), которое будет содержать все категории. Затем я использовал StreamBuilder вместо этого:

StreamBuilder<QuerySnapshot>(
                stream: FirebaseFirestore.instance
                    .collection('LinksData')
                    .where('categories', arrayContains: widget.categoryName)
                    .snapshots(),

                builder: (context, snapshot) {
                  if (snapshot.hasError) {
                    return Center(child: Text('${snapshot.error}'));
                  } else {
                    if (!snapshot.hasData) {
                      return Center(child: CircularProgressIndicator());
                    }
                    return GridView.builder(
                        gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
                          maxCrossAxisExtent: 220,
                          mainAxisSpacing: 12.0,
                        ),
                        itemCount: snapshot.data!.docs.length,
                        itemBuilder: (context, index) {
                          return LinkCards(
                              linkImage: snapshot.data!.docs[index].get('image'),
                              linkTitle: snapshot.data!.docs[index].get('name'),
                              link: snapshot.data!.docs[index].get('link'),
                              relatedCategories: snapshot.data!.docs[index].get('categories'),
                              platform: snapshot.data!.docs[index].get('platform'));
                        });
                  }
                },
              )
person Aamir Zaidi    schedule 01.06.2021