I use provider
library state management for doing add to cart and basically i am a bit beginner in provider
. So the issue i am facing is for example there are three products laptop
, iphone x
& keyboard
. Now if i put laptop
two times in the cart then in cart page
it displays two laptop card
widgets, instead i want to display only one card
widget in that laptop qty: 2
. And second issue is that i have implemented
and -
button in each card
widget in cart page
and if i click on
or -
button then it should reflect on qty
and also on total price
. Really appreciate if you help me in this problem.
main.dart
void main() {
runApp(ChangeNotifierProvider(
create: (context) => Cart(),
child: MyApp(),
));
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
debugShowCheckedModeBanner: false,
);
}
}
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final List<Item> items = [
Item(title: 'laptop ', price: 500.0),
Item(title: 'iphone x ', price: 400.0),
Item(title: 'keyboard ', price: 40.0),
];
@override
Widget build(BuildContext context) {
return Consumer<Cart>(builder: (context, cart, child) {
return Scaffold(
appBar: AppBar(
title: Text('Shopping cart'),
actions: <Widget>[
Padding(
padding: EdgeInsets.all(8.0),
child: Row(
children: <Widget>[
IconButton(
icon: Icon(
Icons.shopping_cart,
color: Colors.white,
),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => CheckoutPage()));
},
),
Text(cart.count.toString())
],
),
)
],
centerTitle: true,
),
body: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(items[index].title),
subtitle: Text(items[index].price.toString()),
trailing: Icon(Icons.add),
onTap: () {
cart.add(items[index]);
},
);
},
),
);
});
}
}
CheckoutPage.dart
class CheckoutPage extends StatefulWidget {
@override
_CheckoutPageState createState() => _CheckoutPageState();
}
class _CheckoutPageState extends State<CheckoutPage> {
@override
Widget build(BuildContext context) {
return Consumer<Cart>(
builder: (context, cart, child) {
return Scaffold(
appBar: AppBar(
title: Text('Checkout Page [\$ ${cart.totalPrice}]'),
actions: [
TextButton(
onPressed: () {
print(cart.totalPrice);
},
child: Text('Check'))
],
),
body: cart.basketItems.length == 0
? Text('no items in your cart')
: ListView.builder(
itemCount: cart.basketItems.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
title: Text(cart.basketItems[index].title),
subtitle: Row(
children: [
TextButton(onPressed: () {}, child: Text(' ')),
Text(cart.basketItems[index].qty.toString()),
TextButton(onPressed: () {}, child: Text('-')),
],
),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () {
cart.remove(cart.basketItems[index]);
},
),
),
);
},
));
},
);
}
}
Item.dart
class Item {
String title;
double price;
Item({this.title, this.price});
}
Cart.dart
class Cart extends ChangeNotifier {
List<Item> _items = [];
double _totalPrice = 0.0;
void add(Item item) {
_items.add(item);
_totalPrice = item.price;
notifyListeners();
}
void remove(Item item) {
_totalPrice -= item.price;
_items.remove(item);
notifyListeners();
}
int get count {
return _items.length;
}
double get totalPrice {
return _totalPrice;
}
List<Item> get basketItems {
return _items;
}
}
CodePudding user response:
Hmm try before adding item add a certain function that will look up for the duplicate item like this
e.g. inside on add
Add qty on you class on item.dart so that in every add item you should have default qty to one then goes this below.
class Item {
String title;
double price;
int qty;
Item({this.title, this.price,this.qty});
}
void add(Item item) {
final itemIsExist = _items.where((e)=> e.title == item.title);
if(itemIsExist.isNotEmpty){
// if item exist and you want to add 1 on qty
final addQty = _items.firstWhere((e)=> e.title == item.title);
addQty.qty= addQty.qty 1;
// do your thing here to calculate again the total
}else{
_items.add(item);
_totalPrice = item.price;
notifyListeners();
}
}
CodePudding user response:
I suggest creating another variable on base class and extend it for model, But now let's follow your way.
We can create a map
to iterate items on _CheckoutPageState
and create a Set
, but we need to count the item quantity,
We can take the help of map in this case and place it just under Consumer builder
before returning Scaffold
Map<String, int> itemsMap = {};
for (final item in cart._items) {
if (!itemsMap.containsKey(item.title)) {
itemsMap.putIfAbsent(item.title, () => 1);
} else {
itemsMap.update(item.title, (value) => itemsMap[item.title]! 1);
}
}
And uses will be like
itemBuilder: (context, index) {
final keys = itemsMap.keys.toList();
final count = itemsMap.values.toList();
return Card(
child: ListTile(
title: Text(keys[index].toString()),
subtitle: Row(
children: [
TextButton(onPressed: () {}, child: Text(' ')),
Text(count[index].toString()),
TextButton(onPressed: () {}, child: Text('-')),
],
),
State class
class _CheckoutPageState extends State<CheckoutPage> {
@override
Widget build(BuildContext context) {
return Consumer<Cart>(
builder: (context, cart, child) {
Map<String, int> itemsMap = {};
for (final item in cart.basketItems) {
if (!itemsMap.containsKey(item.title)) {
itemsMap.putIfAbsent(item.title, () => 1);
} else {
itemsMap.update(item.title, (value) => itemsMap[item.title]! 1);
}
}
return Scaffold(
appBar: AppBar(
title: Text('Checkout Page [\$ ${cart.totalPrice}]'),
actions: [
TextButton(
onPressed: () {
print(cart.totalPrice);
},
child: Text('Check'))
],
),
body: cart.basketItems.length == 0
? Text('no items in your cart')
: ListView.builder(
itemCount: itemsMap.length,
itemBuilder: (context, index) {
final keys = itemsMap.keys.toList();
final count = itemsMap.values.toList();
return Card(
child: ListTile(
title: Text(keys[index].toString()),
subtitle: Row(
children: [
TextButton(
onPressed: () {
cart.add(
Item(
title: keys[index].toString(),
price: keys[index].trim() == "laptop"
? 500
: keys[index].trim() == "iphone x"
? 400
: 40,
),
);
},
child: Text(' ')),
Text(count[index].toString()),
TextButton(
onPressed: () {
cart.remove(Item(
title: keys[index].toString(),
price: keys[index].trim() == "laptop"
? 500
: keys[index].trim() == "iphone x"
? 400
: 40,
));
},
child: Text('-')),
],
),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () {
cart.remove(cart.basketItems[
index]); // remove match all on remove method
},
),
),
);
},
));
},
);
}
}