diff --git a/packages/flutter_shopping_cart/lib/src/widgets/default_shopping_cart_item.dart b/packages/flutter_shopping_cart/lib/src/widgets/default_shopping_cart_item.dart index 51c9091..ba9f89c 100644 --- a/packages/flutter_shopping_cart/lib/src/widgets/default_shopping_cart_item.dart +++ b/packages/flutter_shopping_cart/lib/src/widgets/default_shopping_cart_item.dart @@ -9,6 +9,7 @@ class DefaultShoppingCartItem extends StatelessWidget { const DefaultShoppingCartItem({ required this.product, required this.configuration, + required this.onItemAddedRemoved, super.key, }); @@ -17,112 +18,115 @@ class DefaultShoppingCartItem extends StatelessWidget { /// Shopping cart configuration. final ShoppingCartConfig configuration; + + /// Function that is called when an item is added or removed. + final Function() onItemAddedRemoved; @override Widget build(BuildContext context) { var theme = Theme.of(context); - return ListenableBuilder( - listenable: configuration.service, - builder: (context, _) => Padding( - padding: const EdgeInsets.only(bottom: 20), - child: ListTile( - contentPadding: const EdgeInsets.only(top: 3, left: 4, bottom: 3), - title: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - product.name, - style: theme.textTheme.titleMedium, - ), - IconButton( - padding: EdgeInsets.zero, - constraints: const BoxConstraints(), - onPressed: () async { - await showModalBottomSheet( - context: context, - backgroundColor: theme.colorScheme.surface, - builder: (context) => ProductItemPopup( - product: product, - configuration: configuration, - ), - ); - }, - icon: Icon( - Icons.info_outline, - color: theme.colorScheme.primary, - ), - ), - ], - ), - leading: ClipRRect( - borderRadius: BorderRadius.circular(6), - child: Image.network( - product.imageUrl, + return Padding( + padding: const EdgeInsets.only(bottom: 20), + child: ListTile( + contentPadding: const EdgeInsets.only(top: 3, left: 4, bottom: 3), + title: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + product.name, + style: theme.textTheme.titleMedium, ), - ), - trailing: Column( - children: [ - Row( - mainAxisSize: MainAxisSize.min, - children: [ - if (product.hasDiscount && product.discountPrice != null) ...[ - Text( - product.discountPrice!.toStringAsFixed(2), - style: theme.textTheme.labelSmall, - ), - ] else ...[ - Text( - product.price.toStringAsFixed(2), - style: theme.textTheme.labelSmall, - ), - ], - ], + IconButton( + padding: EdgeInsets.zero, + constraints: const BoxConstraints(), + onPressed: () async { + await showModalBottomSheet( + context: context, + backgroundColor: theme.colorScheme.surface, + builder: (context) => ProductItemPopup( + product: product, + configuration: configuration, + ), + ); + }, + icon: Icon( + Icons.info_outline, + color: theme.colorScheme.primary, ), - Row( - mainAxisAlignment: MainAxisAlignment.end, - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - constraints: const BoxConstraints(), - padding: EdgeInsets.zero, - icon: const Icon( - Icons.remove, - color: Colors.black, - ), - onPressed: () => - configuration.service.removeOneProduct(product), + ), + ], + ), + leading: ClipRRect( + borderRadius: BorderRadius.circular(6), + child: Image.network( + product.imageUrl, + ), + ), + trailing: Column( + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (product.hasDiscount && product.discountPrice != null) ...[ + Text( + product.discountPrice!.toStringAsFixed(2), + style: theme.textTheme.labelSmall, ), - Padding( - padding: const EdgeInsets.all(2), - child: Container( - alignment: Alignment.center, - decoration: BoxDecoration( - color: theme.colorScheme.primary, - borderRadius: BorderRadius.circular(4), - ), - height: 30, - width: 30, - child: Text( - "${product.quantity}", - style: theme.textTheme.titleSmall, - textAlign: TextAlign.center, - ), - ), - ), - IconButton( - constraints: const BoxConstraints(), - padding: EdgeInsets.zero, - icon: const Icon( - Icons.add, - color: Colors.black, - ), - onPressed: () { - configuration.service.addProduct(product); - }, + ] else ...[ + Text( + product.price.toStringAsFixed(2), + style: theme.textTheme.labelSmall, ), ], - ), - ], - ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + constraints: const BoxConstraints(), + padding: EdgeInsets.zero, + icon: const Icon( + Icons.remove, + color: Colors.black, + ), + onPressed: () { + configuration.service.removeOneProduct(product); + onItemAddedRemoved(); + }, + ), + Padding( + padding: const EdgeInsets.all(2), + child: Container( + alignment: Alignment.center, + decoration: BoxDecoration( + color: theme.colorScheme.primary, + borderRadius: BorderRadius.circular(4), + ), + height: 30, + width: 30, + child: Text( + "${product.quantity}", + style: theme.textTheme.titleSmall, + textAlign: TextAlign.center, + ), + ), + ), + IconButton( + constraints: const BoxConstraints(), + padding: EdgeInsets.zero, + icon: const Icon( + Icons.add, + color: Colors.black, + ), + onPressed: () { + configuration.service.addProduct(product); + onItemAddedRemoved(); + }, + ), + ], + ), + ], ), ), ); diff --git a/packages/flutter_shopping_cart/lib/src/widgets/shopping_cart_screen.dart b/packages/flutter_shopping_cart/lib/src/widgets/shopping_cart_screen.dart index 4261e9e..3337de1 100644 --- a/packages/flutter_shopping_cart/lib/src/widgets/shopping_cart_screen.dart +++ b/packages/flutter_shopping_cart/lib/src/widgets/shopping_cart_screen.dart @@ -6,7 +6,7 @@ import "package:flutter_shopping_cart/src/widgets/default_shopping_cart_item.dar import "package:flutter_shopping_cart/src/widgets/default_sum_bottom_sheet_builder.dart"; /// Shopping cart screen widget. -class ShoppingCartScreen extends StatelessWidget { +class ShoppingCartScreen extends StatefulWidget { /// Creates a shopping cart screen. const ShoppingCartScreen({ required this.configuration, @@ -16,26 +16,31 @@ class ShoppingCartScreen extends StatelessWidget { /// Configuration for the shopping cart screen. final ShoppingCartConfig configuration; + @override + State createState() => _ShoppingCartScreenState(); +} + +class _ShoppingCartScreenState extends State { @override Widget build(BuildContext context) { var theme = Theme.of(context); return Scaffold( - appBar: - configuration.appBarBuilder?.call(context) ?? const DefaultAppbar(), + appBar: widget.configuration.appBarBuilder?.call(context) ?? + const DefaultAppbar(), body: SafeArea( child: Stack( fit: StackFit.expand, children: [ Padding( - padding: configuration.pagePadding, + padding: widget.configuration.pagePadding, child: SingleChildScrollView( child: Column( children: [ - if (configuration.titleBuilder != null) ...{ - configuration.titleBuilder!( + if (widget.configuration.titleBuilder != null) ...{ + widget.configuration.titleBuilder!( context, - configuration.translations.cartTitle, + widget.configuration.translations.cartTitle, ), } else ...{ Padding( @@ -45,7 +50,7 @@ class ShoppingCartScreen extends StatelessWidget { child: Row( children: [ Text( - configuration.translations.cartTitle, + widget.configuration.translations.cartTitle, style: theme.textTheme.titleLarge, textAlign: TextAlign.start, ), @@ -53,30 +58,32 @@ class ShoppingCartScreen extends StatelessWidget { ), ), }, - ListenableBuilder( - listenable: configuration.service, - builder: (context, _) => Column( - children: [ - for (var product in configuration.service.products) - configuration.productItemBuilder?.call( - context, - product, - configuration, - ) ?? - DefaultShoppingCartItem( - product: product, - configuration: configuration, - ), + Column( + children: [ + for (var product + in widget.configuration.service.products) + widget.configuration.productItemBuilder?.call( + context, + product, + widget.configuration, + ) ?? + DefaultShoppingCartItem( + product: product, + configuration: widget.configuration, + onItemAddedRemoved: () { + setState(() {}); + }, + ), - // Additional whitespace at - // the bottom to make sure the last - // product(s) are not hidden by the bottom sheet. - SizedBox( - height: configuration.confirmOrderButtonHeight + - configuration.sumBottomSheetHeight, - ), - ], - ), + // Additional whitespace at + // the bottom to make sure the last + // product(s) are not hidden by the bottom sheet. + SizedBox( + height: + widget.configuration.confirmOrderButtonHeight + + widget.configuration.sumBottomSheetHeight, + ), + ], ), ], ), @@ -85,7 +92,7 @@ class ShoppingCartScreen extends StatelessWidget { Align( alignment: Alignment.bottomCenter, child: _BottomSheet( - configuration: configuration, + configuration: widget.configuration, ), ), ], @@ -106,28 +113,19 @@ class _BottomSheet extends StatelessWidget { Widget build(BuildContext context) => Column( mainAxisSize: MainAxisSize.min, children: [ - ListenableBuilder( - listenable: configuration.service, - builder: (BuildContext context, Widget? child) => - configuration.sumBottomSheetBuilder - ?.call(context, configuration) ?? - DefaultSumBottomSheetBuilder( - configuration: configuration, - ), - ), - ListenableBuilder( - listenable: configuration.service, - builder: (context, _) => - configuration.confirmOrderButtonBuilder?.call( - context, - configuration, - configuration.onConfirmOrder, - ) ?? - DefaultConfirmOrderButton( - configuration: configuration, - onConfirmOrder: configuration.onConfirmOrder, - ), - ), + configuration.sumBottomSheetBuilder?.call(context, configuration) ?? + DefaultSumBottomSheetBuilder( + configuration: configuration, + ), + configuration.confirmOrderButtonBuilder?.call( + context, + configuration, + configuration.onConfirmOrder, + ) ?? + DefaultConfirmOrderButton( + configuration: configuration, + onConfirmOrder: configuration.onConfirmOrder, + ), ], ); }