How to get data from multiple firebase collections using a Stream Builder

Viewed 454

My flutter app

I am having problems accessing data from two different collections in firebase using a stream builder. I have a StatefulWidget that's called ForumPage, I have a picture of it above. The ForumPage has a column which has multiple children, one of them is a MyStreamBuilder StatelessWidget. This is the widget I am using to connect to firebase.

MyStreamBuilder

import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:hotel_for_dogs/Posts/need_post.dart';
import 'package:hotel_for_dogs/Posts/sitter_post.dart';

class MyStreamBuilder extends StatelessWidget {
  final String _typeOfPost;
  final String _state;
  final String _city;
  MyStreamBuilder(this._typeOfPost, this._state, this._city);

  @override
  Widget build(BuildContext context) {
    Stream stream = FirebaseFirestore.instance.collection(_typeOfPost).where('state', isEqualTo: _state).where('city', isEqualTo: _city).snapshots();
    return SizedBox(
        height: 400,
        child: StreamBuilder<QuerySnapshot>(
          // needPosts
        stream: stream,
        builder: (context, snapshot) {
          if (snapshot.hasData && snapshot.data.size > 0) {
            final List<DocumentSnapshot> documents = snapshot.data.docs;
            if(_typeOfPost == "needPosts") {
              return ListView(
                  children: documents
                      .map((doc) => Card( child: NeedPost(
                      doc['title'],
                      doc['dogBreed'],
                      doc['dogNeeds'],
                      doc['amountOfTime'],
                      doc['amountPerHour'],
                      doc['pottyTrained'],
                      doc['animalFriendly'],
                      doc['date'],
                      doc['state'],
                      doc['city'],
                      doc['dogName'],
                      doc['email'],
                      doc['phone'],
                      doc['fullName'])
                  ))
                      .toList());
            } else {
              return ListView(
                  children: documents
                      .map((doc) => Card( child: SitterPost(
                      doc['title'],
                      doc['amountPerHour'],
                      doc['date'],
                      doc['state'],
                      doc['city'],
                      doc['email'],
                      doc['phone'],
                      doc['fullName'],
                      doc['breedSize'],
                      doc['bio'],
                      doc['fencedBackYard'],
                      doc['otherAnimals'])
                  ))
                      .toList());
            }
          } else if (snapshot.hasError) {
            return Text("error");
          } else if(!snapshot.hasData){
            return Center(
              child: CircularProgressIndicator(),
            );
          } else {
            return Text("No Data");
          }
            }));
  }
}

When a user types in a state and city, they can then click on the "I am a sitter" button and then click on search. Posts of people who need a sitter will appear (if there is data of course). When a user clicks on the "Need a sitter" button, then posts of people who are sitters will appear. The choosing of which kind of post to display is handled when the search button is hit. Here is the code for that and another picture!

My flutter app with some data entered

 setState(() {
                      if (iAmSitterColor == Colors.black && stateController.text.isNotEmpty && cityController.text.isNotEmpty) {
                        typeOfPost = "needPosts";
                        state = stateController.text;
                        city = cityController.text;
                      } else if (needASitterColor == Colors.black && stateController.text.isNotEmpty && cityController.text.isNotEmpty) {
                        typeOfPost = "sitterPosts";
                        state = stateController.text;
                        city = cityController.text;
                      }
                    });

Setting the state triggers the ui to rebuild so MyStreamBuilder will get the updated info.

MyStreamBuilder(typeOfPost, state, city)

As you may have noticed from the last picture this code works and it does grab data correctly from the firestore. However, if I switch between the two buttons and hit search for a BRIEF moment a red screen will appear and then the posts will appear. I get this error when that happens in my terminal

The following StateError was thrown building StreamBuilder<QuerySnapshot>(dirty, state: _StreamBuilderBaseState<QuerySnapshot, AsyncSnapshot<QuerySnapshot>>#fdcd8):
Bad state: field does not exist within the DocumentSnapshotPlatform

I am wondering is there a better way of doing this? I also tired changing MyStreamBuilder to this:

import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:hotel_for_dogs/Posts/need_post.dart';
import 'package:hotel_for_dogs/Posts/sitter_post.dart';

class MyStreamBuilder extends StatelessWidget {
  final String _typeOfPost;
  final String _state;
  final String _city;
  MyStreamBuilder(this._typeOfPost, this._state, this._city);

  @override
  Widget build(BuildContext context) {
    Stream stream1 = FirebaseFirestore.instance.collection("needPosts").where('state', isEqualTo: _state).where('city', isEqualTo: _city).snapshots();
    Stream stream2 = FirebaseFirestore.instance.collection("sitterPosts").where('state', isEqualTo: _state).where('city', isEqualTo: _city).snapshots();

    if (_typeOfPost == "needPosts"){
      return SizedBox(
          height: 400,
          child: StreamBuilder<QuerySnapshot>(
            // needPosts
              stream: stream1,
              builder: (context, snapshot) {
                if (snapshot.hasData && snapshot.data.size > 0) {
                  final List<DocumentSnapshot> documents = snapshot.data.docs;
                  return ListView(
                      children: documents
                          .map((doc) => Card( child: NeedPost(
                          doc['title'],
                          doc['dogBreed'],
                          doc['dogNeeds'],
                          doc['amountOfTime'],
                          doc['amountPerHour'],
                          doc['pottyTrained'],
                          doc['animalFriendly'],
                          doc['date'],
                          doc['state'],
                          doc['city'],
                          doc['dogName'],
                          doc['email'],
                          doc['phone'],
                          doc['fullName'])
                      ))
                          .toList());
                } else if (snapshot.hasError) {
                  return Text("error");
                } else if(!snapshot.hasData){
                  return Center(
                    child: CircularProgressIndicator(),
                  );
                } else {
                  return Text("No Data");
                }
              }));
    } else {
      return SizedBox(
          height: 400,
          child: StreamBuilder<QuerySnapshot>(
            // needPosts
              stream: stream2,
              builder: (context, snapshot) {
                if (snapshot.hasData && snapshot.data.size > 0) {
                  final List<DocumentSnapshot> documents = snapshot.data.docs;
                  return ListView(
                      children: documents
                          .map((doc) => Card( child: SitterPost(
                          doc['title'],
                          doc['amountPerHour'],
                          doc['date'],
                          doc['state'],
                          doc['city'],
                          doc['email'],
                          doc['phone'],
                          doc['fullName'],
                          doc['breedSize'],
                          doc['bio'],
                          doc['fencedBackYard'],
                          doc['otherAnimals'])
                      ))
                          .toList());
                } else if (snapshot.hasError) {
                  return Text("error");
                } else if(!snapshot.hasData){
                  return Center(
                    child: CircularProgressIndicator(),
                  );
                } else {
                  return Text("No Data");
                }
              }));
    }
  }
}

It gives me the same error. Should I be using future builder? I have only seen future builder being used to access a specific documents data within a collection. What I need is all of the documents data within a collection.

1 Answers

This is happening because you are not properly geting the data from the firestore to be set on you map.

To remove that warning you need to change in your code the doc[FIELD_NAME] structure to doc.data()[FIELD_NAME] during the map setup.

Related