Affechon
Affechon

Reputation: 35

Getting the total price of products in Firestore Flutter

This is my first time asking a question because I've tried everything that I've researched from this website for hours but nothing seems to work. I'm trying to get the total price of my cart but every time the state changes it constantly adds and I just want the total to stay as it is when I'm changing pages. Even clicking a delete button from my cart page, refreshes the function and it adds another value to the total. Thank you so much if someone could help me

Here's the code that I've tried

    Widget build(BuildContext context) {
return Scaffold(
    backgroundColor: white,
    body: StreamBuilder(
      stream: FirebaseFirestore.instance
          .collection("users")
          .doc(user.uid)
          .collection("cart")
          .snapshots(),
      builder:
          (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
        FirebaseFirestore.instance
            .collection("users")
            .doc(user.uid)
            .collection("cart")
            .get()
            .then((querySnapshot) {
          querySnapshot.docs.forEach((result) {
            total += result.data()['price'];
          });
        });

Upvotes: 2

Views: 988

Answers (3)

Peter Obiechina
Peter Obiechina

Reputation: 2835

The problem is that total value keeps on increasing whenever stream builder is called. Also, you do not need to listen to stream and still call get(). The stream returns similar value to the get.

Change your code to this

return Scaffold(
  body: StreamBuilder(
    stream: FirebaseFirestore.instance
        .collection("users")
        .doc(user.uid)
        .collection("cart")
        .snapshots(),
    builder: (BuildContext context, snapshot) {
      if (snapshot.hasError) return Text('Something went wrong');
      if (snapshot.connectionState == ConnectionState.waiting)
        return CircularProgressIndicator();

      // NB: I set the value of total = 0; so that anytime the stream
      // builder is called, total starts from 0.
      total = 0;
      snapshot.data.docs.forEach((result) {
        total += result.data()['price'];
      });
      print(total);
      print('done');
      return Text('done');
    },
  ),
);

On the other hand, you can still call this function using futureBuilder.


return Scaffold(
  body: FutureBuilder(
    future: FirebaseFirestore.instance
        .collection("users")
        .doc(user.uid)
        .collection("cart")
        .get(),
    builder: (BuildContext context, snapshot) {
      if (snapshot.hasError) return Text('Something went wrong');
      if (snapshot.connectionState == ConnectionState.waiting)
        return CircularProgressIndicator();

      total = 0;
      snapshot.data.docs.forEach((result) {
        total += result.data()['price'];
      });
      print(total);
      print('done');
      return Text('done');
    },
  ),
);

The difference between stream and future builder is that future builder is only called once. Stream builder is called whenever the data changes.

Upvotes: 1

esentis
esentis

Reputation: 4666

Welcome!

First of all, you are using a StreamBuilder and even though you provide a Stream, you call the stream again at builder method, you can can access the value of the Stream through the snapshot. Moreover, your builder returns no Widget but just iterates through the items of the Stream, to calculate I guess the total price. Instead, you can change the builder method to something like this

    Widget build(BuildContext context) {
return Scaffold(
    backgroundColor: white,
    body: StreamBuilder(
      stream: FirebaseFirestore.instance
          .collection("users")
          .doc(user.uid)
          .collection("cart")
          .snapshots(),
      builder:
          (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
       var data = snapshot.data;
       var totalCartPrice = data.fold(0,(prev, element)=>prev['price']+element['price']);
       return Text(totalCartPrice);
        });

Instead of using fold method you can iterate the data and do the calculations by hand. But I find it more elegant.

There was no reason in calling setState aswell, the StreamBuilder fires changes if anything changes in the Firestore document.

Upvotes: 1

Hazar Belge
Hazar Belge

Reputation: 1089

First of all why do you send a request inside builder again? There is already a variable called "snapshot". You can get data with snapshot.data.docs.

Secondly, you are trying to increase total value every time without reset. If you set total variable to 0 before adding the prices probably it will solve your problem.

 Widget build(BuildContext context) {
   return Scaffold(
     backgroundColor: white,
     body: StreamBuilder(
       stream: FirebaseFirestore.instance
          .collection("users")
          .doc(user.uid)
          .collection("cart")
          .snapshots(),
      builder:(BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
         if(snapshot.connectionState == ConnectionState.waiting) {
            return CircularProgressIndicator();
         } else {
            total = 0;
            snapshot.data.docs.forEach((result) {
               total += result.data()['price'];
            });
            return WidgetYouWantToUse();
         }
    }

Upvotes: 1

Related Questions