Flutter nested firebase istances

Viewed 55

Can someone explain me why the productlist variable is null when this class run? It loooks like the variable is being populated after the first istance end, so the CardShopList widget hasn't the productlist that is coming from the nested istance. Have you got some tips? Thanks!

  Future getMyShopLists(User user) async {
    List<Widget> listItems = [];

    FirebaseFirestore.instance
        .collection('user')
        .doc(user.uid)
        .collection('shoplist')
        .get()
        .then((event) {
      event.docs.forEach((shoplist) {
        List<ProductModel> productlist = [];
        Map shopListData = shoplist.data();

        shoplist.reference.collection('productlist').get().then((element) {
          Map productData = shoplist.data();
          element.docs.forEach((doc) {
            productlist.add(ProductModel(
              id: doc.id,
              name: productData['name'],
              quantity: productData['quantity'],
              price: productData['price'],
            ));
          });
        });

        listItems.add(CardShopList(
          shoplist.id,
          shopListData['name'],
          productlist, // <------------------------------- THIS IS NULL!!!!
          getMyShopLists,
          key: ValueKey<String>(shoplist.id),
        ));
      });
      if (this.mounted) {
        setState(() {
          shopListsWidget = listItems;
        });
      }
    });

    return shopListsWidget;
  }

EDIT i use that class in this FutureBuilder :

FutureBuilder(
            future:
                searchIcon.icon == Icons.search ? getMyShopLists(user) : null,
            builder: (BuildContext context, AsyncSnapshot snapshot) {
              if (snapshot.hasData) {
                return isShopListEmpty
                    ? new Center(
                        child: new Text(
                          "x",
                          style: TextStyle(fontSize: 20),
                          textAlign: TextAlign.center,
                        ),
                      )
                    : Container(
                        child: Column(
                          children: <Widget>[
                            Expanded(
                                child: ListView.builder(
                                    itemCount: shopListsWidget.length,
                                    physics: BouncingScrollPhysics(),
                                    itemBuilder: (context, index) {
                                      return shopListsWidget[index];
                                    }))
                          ],
                        ),
                      );
              } else {
                return Center(
                  child: CircularProgressIndicator(),
                );
              }
            },
          ),
3 Answers

The "productlist" is not expected to be filled because you are using foreach. There may be alternative solutions, but turning foreach to for will probably fix it.

 ...
   await FirebaseFirestore.instance //added await
        .collection('user')
        .doc(user.uid)
        .collection('shoplist')
        .get()
        .then((event) async{ //added async
        for(QueryDocumentSnapshot shoplist in event.docs){ //---this line changed
        List<ProductModel> productlist = [];
        Map shopListData = shoplist.data();

        await shoplist.reference.collection('productlist').get().then((element) //added await 
{
        Map productData = shoplist.data();
        for(QueryDocumentSnapshot doc in element.docs) //---this line changed
         {
            productlist.add(ProductModel(
              id: doc.id,
              name: productData['name'],
              quantity: productData['quantity'],
              price: productData['price'],
            ));
         }       
        });

        listItems.add(CardShopList(
          shoplist.id,
          shopListData['name'],
          productlist,
          getMyShopLists,
          key: ValueKey<String>(shoplist.id),
        ));
      }
     ...

Keep in mind I did not tested this, but I had previous issues with that.

My suggestion is to move this block of code:

listItems.add(CardShopList(
      shoplist.id,
      shopListData['name'],
      productlist, // <------------------------------- THIS IS NULL!!!!
      getMyShopLists,
      key: ValueKey<String>(shoplist.id),
    ));

Inside the firebase query like so:

   shoplist.reference.collection('productlist').get().then((element) {
      Map productData = shoplist.data();
      element.docs.forEach((doc) {
        productlist.add(ProductModel(
          id: doc.id,
          name: productData['name'],
          quantity: productData['quantity'],
          price: productData['price'],
        ));
      });

      listItems.add(CardShopList(
        shoplist.id,
        shopListData['name'],
        productlist,
        getMyShopLists,
        key: ValueKey<String>(shoplist.id),
      ));

    });

The reason for the null value is that the firebase query has a local variables and they exist only for the moment of the request. The only way to work with them is locally in the query callback.

You're defining it as an async function, but didn't await for the queries to be processed in order, can you try adding await in these locations:

//here #1
await FirebaseFirestore.instance
        .collection('user')
        .doc(user.uid)
        .collection('shoplist')
        .get()
        .then((event) async {
      event.docs.forEach((shoplist) {
        List<ProductModel> productlist = [];
        Map shopListData = shoplist.data();
   //here#2
        await shoplist.reference.collection('productlist').get().then((element) {
          Map productData = shoplist.data();
          element.docs.forEach((doc) {
            productlist.add(ProductModel(
              id: doc.id,
              name: productData['name'],
              quantity: productData['quantity'],
              price: productData['price'],
            ));
          });
        });
Related