From 2e0ce1f223af646c42bf762522fa0dd8c1263985 Mon Sep 17 00:00:00 2001 From: Jonas Franz Date: Mon, 21 Feb 2022 22:11:33 +0100 Subject: [PATCH] Add cart store --- lib/app.dart | 2 + lib/screens/cart/cart_screen.dart | 9 ++- lib/screens/cart/total_price_text.dart | 7 +- lib/screens/product_list/cart_button.dart | 6 +- lib/screens/product_list/product_item.dart | 11 +++- lib/store/cart_store.dart | 77 ++++++++++++++++++++++ 6 files changed, 106 insertions(+), 6 deletions(-) create mode 100644 lib/store/cart_store.dart diff --git a/lib/app.dart b/lib/app.dart index 1f9c728..607d428 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -8,6 +8,7 @@ import 'package:thesis_shop/store/product_store.dart'; import 'package:thesis_shop/store/user_store.dart'; import 'package:thesis_shop/utils/map_keys_extension.dart'; +import 'store/cart_store.dart'; import 'store/product_loading_store.dart'; class ThesisShopApp extends StatelessWidget { @@ -22,6 +23,7 @@ class ThesisShopApp extends StatelessWidget { ChangeNotifierProvider(create: (context) => UserStore()), ProductLoadingStoreProvider(productService: productService), ProductStoreProvider(), + CartStoreProvider(), ], child: MaterialApp( title: 'Thesis Shop', diff --git a/lib/screens/cart/cart_screen.dart b/lib/screens/cart/cart_screen.dart index e369485..6874175 100644 --- a/lib/screens/cart/cart_screen.dart +++ b/lib/screens/cart/cart_screen.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import 'package:thesis_shop/models/cart_item.dart'; import 'package:thesis_shop/models/product.dart'; import 'package:thesis_shop/screens/cart/total_price_text.dart'; +import 'package:thesis_shop/store/cart_store.dart'; import 'package:thesis_shop/widgets/user_switch.dart'; import 'cart_item_list.dart'; @@ -17,6 +19,7 @@ class CartScreen extends StatelessWidget { @override Widget build(BuildContext context) { + final items = context.watch().cart; return Scaffold( appBar: AppBar( title: const Text('Warenkorb'), @@ -24,9 +27,9 @@ class CartScreen extends StatelessWidget { ), body: Column( mainAxisSize: MainAxisSize.max, - children: const [ - Expanded(child: CartItemList(items: _placeHolderItems)), - TotalPriceText(), + children: [ + Expanded(child: CartItemList(items: items)), + const TotalPriceText(), ], ), ); diff --git a/lib/screens/cart/total_price_text.dart b/lib/screens/cart/total_price_text.dart index a08b1e6..2aa5950 100644 --- a/lib/screens/cart/total_price_text.dart +++ b/lib/screens/cart/total_price_text.dart @@ -1,15 +1,20 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:thesis_shop/store/cart_store.dart'; class TotalPriceText extends StatelessWidget { const TotalPriceText({Key? key}) : super(key: key); @override Widget build(BuildContext context) { + final totalPrice = context + .select((store) => store.totalPrice) + .toStringAsFixed(2); return SafeArea( child: Padding( padding: const EdgeInsets.all(8.0), child: Text( - 'Gesamtpreis: 27€', + 'Gesamtpreis: $totalPrice', style: Theme.of(context).textTheme.labelLarge?.copyWith(fontSize: 24), ), ), diff --git a/lib/screens/product_list/cart_button.dart b/lib/screens/product_list/cart_button.dart index 5256e5e..66e6be1 100644 --- a/lib/screens/product_list/cart_button.dart +++ b/lib/screens/product_list/cart_button.dart @@ -1,17 +1,21 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import 'package:thesis_shop/benchmark_counter.dart'; import 'package:thesis_shop/route_key.dart'; +import 'package:thesis_shop/store/cart_store.dart'; class CartButton extends StatelessWidget { const CartButton() : super(key: const Key('cart_button')); @override Widget build(BuildContext context) { + final numberOfProducts = + context.select((store) => store.numberOfProducts); BenchmarkCounters.cartButton++; return ElevatedButton.icon( onPressed: () => Navigator.of(context).pushRouteKey(RouteKey.cart), icon: const Icon(Icons.shopping_basket), - label: const Text('Warenkorb (3 Produkte)'), + label: Text('Warenkorb ($numberOfProducts Produkte)'), ); } } diff --git a/lib/screens/product_list/product_item.dart b/lib/screens/product_list/product_item.dart index 62e813d..f61e3b4 100644 --- a/lib/screens/product_list/product_item.dart +++ b/lib/screens/product_list/product_item.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import 'package:thesis_shop/models/product.dart'; +import 'package:thesis_shop/store/cart_store.dart'; import 'package:thesis_shop/widgets/number_picker.dart'; class ProductItem extends StatelessWidget { @@ -8,9 +10,16 @@ class ProductItem extends StatelessWidget { @override Widget build(BuildContext context) { + final numberOfProduct = context + .select((store) => store.amountOfProduct(product)); + final cartStore = context.read(); return ListTile( title: Text('${product.title} (${product.priceAsString}€/Stück)'), - trailing: NumberPicker(value: 5, onUp: () {}, onDown: () {}), + trailing: NumberPicker( + value: numberOfProduct, + onUp: () => cartStore.increaseAmount(product), + onDown: () => cartStore.decreaseAmount(product), + ), ); } } diff --git a/lib/store/cart_store.dart b/lib/store/cart_store.dart new file mode 100644 index 0000000..75ae658 --- /dev/null +++ b/lib/store/cart_store.dart @@ -0,0 +1,77 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:thesis_shop/models/cart_item.dart'; +import 'package:thesis_shop/models/product.dart'; +import 'package:thesis_shop/models/remote_resource.dart'; +import 'package:thesis_shop/store/product_store.dart'; + +class CartStore extends ChangeNotifier { + List _cart = []; + List get cart => _cart; + + int get numberOfProducts => _cart.length; + double get totalPrice => _cart.fold( + 0.0, + (value, item) => value + item.product.price * item.amount, + ); + + int amountOfProduct(Product product) => cart + .firstWhere((item) => product == item.product, + orElse: () => CartItem(product: product, amount: 0)) + .amount; + + void updateProductList(RemoteResource> remoteProducts) { + if (remoteProducts is LoadingRemoteResource) { + return; + } + late final List products; + if (remoteProducts is ErrorRemoteResource) { + products = []; + } else { + products = remoteProducts.asFinished().value; + } + final productsByKey = { + for (final product in products) product.title: product, + }; + _cart = _cart + .where((item) => productsByKey.containsKey(item.product.title)) + .map((item) => CartItem( + product: productsByKey[item.product.title]!, amount: item.amount)) + .toList(); + notifyListeners(); + } + + void _addAmount(Product product, int amount) { + final old = _cart.cast().firstWhere( + (item) => item?.product.title == product.title, + orElse: () => null); + if (old == null) { + if (amount <= 0) return; + _cart = _cart..add(CartItem(product: product, amount: amount)); + notifyListeners(); + return; + } + if (old.amount + amount < 0) return; + final newItem = CartItem(product: product, amount: old.amount + amount); + _cart = _cart + .map( + (item) => item.product.title == product.title ? newItem : item, + ) + .toList(); + notifyListeners(); + } + + void increaseAmount(Product product) => _addAmount(product, 1); + void decreaseAmount(Product product) => _addAmount(product, -1); +} + +class CartStoreProvider + extends ChangeNotifierProxyProvider { + CartStoreProvider({Key? key, Widget? child}) + : super( + key: key, + child: child, + create: (context) => CartStore(), + update: (context, productStore, cartStore) => + cartStore!..updateProductList(productStore.products)); +}