i have a cart page where i need to be able to increase quantity of the items and display the total, the problem is i have to click on the button for increasing quantity twice before it starts to update the total of the items but then the total is displaying the value of if i would’ve normally increased the quantity once.
this is how the cart looks initially, the total is displaying the right result
after increasing the quantity of one of the items once, the total doesnt update and stays as is
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:watch_hub/models/cart.dart';
import 'package:watch_hub/services/database.dart';
class GetPrice {
static num? price = 0;
}
class CartList extends StatefulWidget {
const CartList({super.key});
@override
State<CartList> createState() => _CartListState();
}
class _CartListState extends State<CartList> {
static int? price;
@override
Widget build(BuildContext context) {
final carts = Provider.of<DatabaseService>(context);
double maxWidth = MediaQuery.sizeOf(context).width;
List totalPriceList = [];
int totalQuantity;
return Scaffold(
appBar: AppBar(
title: Text("Cart"),
),
body: Consumer<List<Cart>>(builder: (context, cart, child) {
return ListView.builder(
itemCount: cart.length,
itemBuilder: (
context,
index,
) {
int counter = cart[index].price! * cart[index].quantity!;
totalPriceList.add(counter);
totalPriceList.reduce((value, element) {
GetPrice.price = value + element;
print("reduce value $value");
print("reduce element $element");
});
print("price list is $totalPriceList");
print("quantity list is ${GetPrice.quantity}");
print("total is ${GetPrice.price.toString()}");
return Column(
children: [
Container(
// height: 50,
child: ListTile(
leading: CircleAvatar(
backgroundImage: NetworkImage(
cart[index].image!,
),
),
title: Text(
cart[index].model!,
style: TextStyle(fontSize: 20),
),
subtitle: Text(
cart[index].brand!,
style: TextStyle(fontSize: 20),
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton.filled(
style: IconButton.styleFrom(
backgroundColor: Colors.red),
onPressed: () {
if (cart[index].quantity != 1) {
var cont = context.read<DatabaseService>();
cart[index].quantity =
cart[index].quantity! - 1;
/*ALL THE BELOW METHOD DOES IS CALL THE notifyListeners() METHOD*/
cont.updateQuantity();
}
},
icon: Icon(Icons.remove),
),
Column(
children: [
Text(
cart[index].price.toString(),
style: TextStyle(fontSize: 20),
),
Text(
"QTY: ${GetPrice.quantity.toString()}",
style: TextStyle(fontSize: 16),
)
],
),
IconButton.filled(
style: IconButton.styleFrom(
backgroundColor: Colors.green),
onPressed: () {
var cont = context.read<DatabaseService>();
cart[index].quantity = cart[index].quantity! + 1;
print("increase price is ${GetPrice.price}");
//ALL THE BELOW METHOD DOES IS CALL THE notifyListeners() METHOD
cont.updateQuantity();
},
icon: Icon(Icons.add),
),
],
)),
),
],
);
},
);
}),
bottomSheet: Container(
height: 130,
color: Colors.brown[400],
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
const Padding(
padding: EdgeInsets.all(8.0),
child: Text(
"Total ",
style: TextStyle(fontSize: 25, color: Colors.white),
),
),
Padding(
padding: EdgeInsets.all(8.0),
child: Text(
"$${GetPrice.price.toString()}",
style: const TextStyle(fontSize: 25, color: Colors.white),
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 200,
child: FloatingActionButton(
onPressed: () {},
child: Text("Make Order"),
),
)
],
)
],
),
),
);
}
}
THIS IS THE PARENT WIDGET OF THE ABOVE WIDGET
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:watch_hub/models/cart.dart';
import 'package:watch_hub/models/watch.dart';
import 'package:watch_hub/screens/home/cart_list.dart';
import 'package:watch_hub/screens/home/watch_list.dart';
import 'package:watch_hub/services/database.dart';
class CartProvider extends StatefulWidget {
const CartProvider({super.key});
@override
State<CartProvider> createState() => _CartProviderState();
}
class _CartProviderState extends State<CartProvider> {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => DatabaseService(),
child: StreamProvider<List<Cart>>.value(
value: DatabaseService().carts,
initialData: const [],
child: Scaffold(
body: CartList(),
),
),
);
}
}
Database.dart
final DocumentReference<Map<String, dynamic>> cart =
FirebaseFirestore.instance.collection("Cart").doc(user!.uid);
List<Cart> _cartFromSnapshot(
DocumentSnapshot<Map<String, dynamic>> snapshot) {
List docs = [];
List docs1 = [];
docs.add(snapshot.data()!);
docs1 = docs[0]['cart'];
print("docs1 says $docs1");
return docs1.map((doc) {
print("cart brand is ${doc['brand']}");
return Cart(
brand: doc['brand'] ?? '',
model: doc['model'] ?? '',
price: doc['price'] ?? 0,
image: doc['image'] ?? '',
quantity: doc['quantity'] ?? 0,
);
}).toList();
}
Stream<List<Cart>> get carts {
return cart.snapshots().map(_cartFromSnapshot);
}
2
Answers
Because you are trying to listen to a value from the provider inside an event handler which is onPressed() here. Event handlers are used to trigger actions rather than updating the UI. Just simply create an instance of provider outside the handler in the main widget and then just use your cart parameter of your consumer to trigger UI changes in the handler
Then in your onPressed() handler
I think you are very close but
CartProvider
might be redundant. Here is how I would structure it by looking at the example from Firestore’s docs here: https://firebase.flutter.dev/docs/firestore/usage/#realtime-changes.The example creates a
StreamProvider
which listens to real-time changes from Firestore. So I mainly changedCartList
andDatabase
files to follow this example.CartList
should only display data. Currently, it is trying to calculate the total but the total should come from the Stream/Firebase. I made changes to theonPressed
functions and wrapped the whole thing inStreamProvider
(I removed
GetPrice
class entirely, it was not staying up to date with values from Firestore correctly.)Database.dart
file has to be changed to take care of actually updating data. You already have anupdateQuantity()
function and this should go through the carts and either increment or decrement itsquantity
attributes. I think yours should change to look like this:totalPrice
variable like this:Now when the
CartList
widget is displayingtotalPrice
it should get the latest.updateQuantity
should also update thetotalPrice
.totalPrice
should also be updated. So I added a call inside_cartFromSnapshot
.main.dart
file would look like this:Hope this helps! You can keep CartProvider if you want, just try to put the
ChangeNotifierProvider
at the root of the app.