1

I have a stream builder listening to Firestore, and it works when it comes to fetching data. The issue is that the streambuilder keeps rebuilding when I switch pages. The main page first builds twice, when I scroll to another page it builds twice again, and when I pop back to the main page it rebuilds for a third time and so on. The images are stored in temp storage and I open them with AssetImage()

Here's what I mean: https://i.stack.imgur.com/nkZn4.jpg

If you need a reference heres the tutorial I followed: https://www.youtube.com/watch?v=8PfiY0U_PBI

CODE:

void queryItems() async {
    //!Fetch items
    try {
      Query query = await
          yogurts.collection('current_items').where("available", isEqualTo: true);
      //!Filter available izdelke
      //*QuerySnapshot contains zero or more QueryDocumentSnapshot objects representing the results of a query
      items = query.snapshots().map((list) {
        //!Convert stream to map on the fly ->
        return list.documents.map((doc) {
          //*For every document
          return doc.data; //*Data from every document
        });
      });
      _postStreamController.add(items);
    } catch (e) {
      print("Got error: ${e.error}");
    }
  }

  Container buildSelection(BuildContext context) {
    return Container(
            height: MediaQuery.of(context).size.height * 0.9,  //!Višina containerja je 90% višine ekrana
            decoration: new BoxDecoration(
                boxShadow: [
                  new BoxShadow(
                    color: Colors.black.withOpacity(0.6), //*Black shadow okoli
                    offset: new Offset(1.0, 2.0), //*z offsetom
                    blurRadius: 15.0,
                  )
                ],
                color: THEME_COLOR.withOpacity(0.9), //*Tema
                borderRadius: new BorderRadius.only(
                    //*Oglati robovi
                    bottomLeft: const Radius.circular(55.0),
                    bottomRight: const Radius.circular(55.0))),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.center,
              mainAxisSize: MainAxisSize.max,
              mainAxisAlignment: MainAxisAlignment.start,
              children: <Widget>[
                Padding(
                  //!Vrsta za orders in settings icone
                  padding: const EdgeInsets.only(left: 20, right: 20, top: 25),
                  child: Row(
                    mainAxisSize: MainAxisSize.max,
                    mainAxisAlignment: MainAxisAlignment
                        .spaceBetween, //!Porazdeli ikone levo in desno z spaco-om vmes
                    children: <Widget>[
                      new OrdersIcon(user: user),
                      new SettingsIcon(user: user),
                    ],
                  ),
                ),
                //!Stream builder za grajenje seznama izdelkov iz baze
                StreamBuilder(
                  //!Posluša in rebuilda seznam for every new event
                  stream: items, //?single-subscription stream from the future
                  initialData: [],
                  builder: (context, AsyncSnapshot snap) {
                    //*Rebuilds its childer whenever a new value gets emited by the stream
                    List slideList =
                        snap.data.toList(); //! Convert AsynSnapshot into List
                    print("Number of all items:" + slideList.length.toString());
                    if (snap.connectionState == ConnectionState.waiting) {
                      return Center(
                        child: CircularProgressIndicator(
                          valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
                        ),
                      );
                    } else {
                      return Container(
                        height: MediaQuery.of(context).size.height * 0.7,
                        child: PageView.builder(
                            onPageChanged: (num) {
                              print("Current index: " + num.toString());
                            },
                            controller: itemView, //!Pass PageView controller
                            scrollDirection: Axis.horizontal,
                            itemCount: slideList.length, //!Število elementov je dolžina lista
                            itemBuilder: (context, int currentIndex) {
                              //!Build current facing item
                              bool active = (currentIndex == current_page);
                              //print(active.toString());
                              //print("Trenutni index: " + currentIndex.toString() + " Trenutna stran: " + current_page.toString());
                              //?(Lastnosti izdelka kot Map, bool trenutno aktiven, trenutni facing element)
                              return buildProductListPage(slideList[currentIndex], active, currentIndex);
                            },
                          ),
                      );
                    }
                  },
                ),

              ],
            ),
          );
  }

  //!Build list view of products
  Widget buildProductListPage(Map data, bool active, var index) {
    final double blur = active ? 5 : 0;
    final double offset = active ? 2 : 0;
    final double top = active ? 30 : 200;
    final double iconSize = active ? 50 : 0;
    final String ime = data['file_name'];
    final String path = "${dir.path}/$ime";
    print(path);
    return AnimatedContainer(
      height: 600,
      width: 400,
      duration: Duration(milliseconds: 1200),
      curve: Curves.easeOutQuint,
      margin: EdgeInsets.only(top: top, bottom: 10, right: 15, left: 15),
      decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(20.0),
          image: DecorationImage(
              fit: BoxFit.cover, image: AssetImage(path)),
          boxShadow: [
            BoxShadow(
              color: Colors.black.withOpacity(0.7),
              offset: new Offset(offset / 2, offset),
              blurRadius: blur,
            )
          ]),
      child: Stack(
        children: <Widget>[
          Column(
            children: <Widget>[
              Align(
                alignment: Alignment.topCenter,
                child: Padding(
                  padding: const EdgeInsets.only(top: 15.0),
                  child: Text(
                    data['ime'],
                    style: TextStyle(
                        fontFamily: 'MadeEvolveSans', fontSize: 50, color: WHITE),
                  ),
                ),
              ),
              Align(
                alignment: Alignment.topCenter,
                child: Text(
                  data['volume'].toString() + "ml",
                  style:
                      TextStyle(fontFamily: 'MadeEvolveSans', fontSize: 25, color: WHITE),
                ),
              ),
            ],
          ),
          Align(
            alignment: Alignment.bottomRight,
            child: Padding(
              padding: const EdgeInsets.only(bottom: 15.0, right: 17.0),
              child: IconButton(
                onPressed: () {
                  setState(() {
                    if (selectedItems.containsKey(data['ime'])) {
                      selectedItems.update(data['ime'], (dynamic val) => ++val);
                    } else {
                      selectedItems[data['ime']] = 1;
                    }
                    print(selectedItems);
                  });
                },
                icon: Icon(
                  Icons.add_circle,
                  color: WHITE,
                  size: iconSize,
                ),
              ),
            ),
          ),
          Align(
            alignment: Alignment.bottomCenter,
            child: Padding(
              padding: const EdgeInsets.only(bottom: 0.0),
              child: Text(
                (selectedItems[data['ime']] == 0 || selectedItems[data['ime']] == null)
                    ? " "
                    : selectedItems[data['ime']].toString(),
                style:
                    TextStyle(fontFamily: 'MadeEvolveSans', fontSize: 60, color: WHITE),
              ),
            ),
          ),
          Align(
            alignment: Alignment.bottomLeft,
            child: Padding(
              padding: const EdgeInsets.only(bottom: 15.0, left: 0.0),
              child: IconButton(
                onPressed: () {
                  setState(() {
                    if (selectedItems.containsKey(data['ime'])) {
                      selectedItems.update(data['ime'], (dynamic val) => --val);
                      if (selectedItems[data['ime']] == 0) {
                        selectedItems.remove(data['ime']);
                      }
                    }
                    print(selectedItems);
                  });
                },
                icon: Icon(
                  Icons.do_not_disturb_on,
                  color: WHITE,
                  size: iconSize,
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }

Any help would be appreciated :D

  • Ashes, where is `items` defined? Is `buildSelection` inside a stateless or stateful widget? – Frank Treacy Jan 19 '20 at 16:49
  • Hi, I have a query function that I call in initState(), I pasted it in the code. In the stateful widget – ashesofphoenix Jan 19 '20 at 17:02
  • Here's the link to full code: https://pastebin.com/2EnnDVny – ashesofphoenix Jan 19 '20 at 17:14
  • 2
    Sorry I didn't have much time to look at it. You should assume that any `build` (including `builder`) method WILL be run an arbitrary amount of times. If `PageView` rebuilds a lot, make sure you are correctly caching your stream in the `StatefulWidget`. No matter how many times the builder is called, the same instance of that stream will be used. I wrote about this same problem but for futures (https://flutterigniter.com/future-async-called-multiple-times/) , hopefully you find that more insightful than this comment. Good luck! – Frank Treacy Jan 20 '20 at 03:22
  • Aha I think I understand, will try some things out. Thank you very much :D – ashesofphoenix Jan 20 '20 at 20:44

1 Answers1

0

StreamBuilder only rebuilds when the Stream it's listening to has been updated. You may want to look into how the Stream items is updated or initialized. It's likely that you're updating the Stream during scroll which causes the StreamBuilder to rebuild.

Omatt
  • 8,564
  • 2
  • 42
  • 144