mirror of
https://github.com/Iconica-Development/flutter_shopping.git
synced 2025-05-19 08:53:46 +02:00
feat: add interface to shopping cart
This commit is contained in:
parent
fd8afbde03
commit
1b78b2c674
9 changed files with 159 additions and 285 deletions
|
@ -2,6 +2,5 @@
|
||||||
library flutter_shopping_cart;
|
library flutter_shopping_cart;
|
||||||
|
|
||||||
export "src/config/shopping_cart_config.dart";
|
export "src/config/shopping_cart_config.dart";
|
||||||
export "src/config/shopping_cart_localizations.dart";
|
export "src/config/shopping_cart_translations.dart";
|
||||||
export "src/services/product_service.dart";
|
|
||||||
export "src/widgets/shopping_cart_screen.dart";
|
export "src/widgets/shopping_cart_screen.dart";
|
||||||
|
|
|
@ -1,73 +1,48 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter_shopping/flutter_shopping.dart";
|
import "package:flutter_shopping_cart/flutter_shopping_cart.dart";
|
||||||
import "package:flutter_shopping_cart/src/widgets/product_item_popup.dart";
|
import "package:flutter_shopping_cart/src/widgets/product_item_popup.dart";
|
||||||
|
import "package:flutter_shopping_interface/flutter_shopping_interface.dart";
|
||||||
Widget _defaultNoContentBuilder(BuildContext context) =>
|
|
||||||
const SizedBox.shrink();
|
|
||||||
|
|
||||||
/// Shopping cart configuration
|
/// Shopping cart configuration
|
||||||
///
|
///
|
||||||
/// This class is used to configure the shopping cart.
|
/// This class is used to configure the shopping cart.
|
||||||
class ShoppingCartConfig<T extends Product> {
|
class ShoppingCartConfig {
|
||||||
/// Creates a shopping cart configuration.
|
/// Creates a shopping cart configuration.
|
||||||
ShoppingCartConfig({
|
ShoppingCartConfig({
|
||||||
required this.productService,
|
required this.service,
|
||||||
|
required this.onConfirmOrder,
|
||||||
this.productItemBuilder = _defaultProductItemBuilder,
|
this.productItemBuilder = _defaultProductItemBuilder,
|
||||||
this.onConfirmOrder,
|
this.confirmOrderButtonBuilder = _defaultConfirmOrderButton,
|
||||||
this.confirmOrderButtonBuilder,
|
|
||||||
this.confirmOrderButtonHeight = 100,
|
this.confirmOrderButtonHeight = 100,
|
||||||
this.sumBottomSheetBuilder,
|
this.sumBottomSheetBuilder = _defaultSumBottomSheetBuilder,
|
||||||
this.sumBottomSheetHeight = 100,
|
this.sumBottomSheetHeight = 100,
|
||||||
this.titleBuilder,
|
this.titleBuilder,
|
||||||
this.localizations = const ShoppingCartLocalizations(),
|
this.translations = const ShoppingCartTranslations(),
|
||||||
this.padding = const EdgeInsets.symmetric(horizontal: 32),
|
this.pagePadding = const EdgeInsets.symmetric(horizontal: 32),
|
||||||
this.bottomPadding = const EdgeInsets.fromLTRB(44, 0, 44, 32),
|
this.bottomPadding = const EdgeInsets.fromLTRB(44, 0, 44, 32),
|
||||||
this.appBar,
|
this.appBar = _defaultAppBar,
|
||||||
Widget Function(BuildContext context) noContentBuilder =
|
});
|
||||||
_defaultNoContentBuilder,
|
|
||||||
}) : assert(
|
|
||||||
confirmOrderButtonBuilder != null || onConfirmOrder != null,
|
|
||||||
"""
|
|
||||||
If you override the confirm order button builder,
|
|
||||||
you cannot use the onConfirmOrder callback.""",
|
|
||||||
),
|
|
||||||
assert(
|
|
||||||
confirmOrderButtonBuilder == null || onConfirmOrder == null,
|
|
||||||
"""
|
|
||||||
If you do not override the confirm order button builder,
|
|
||||||
you must use the onConfirmOrder callback.""",
|
|
||||||
),
|
|
||||||
_noContentBuilder = noContentBuilder;
|
|
||||||
|
|
||||||
/// Product Service. The service contains all the products that
|
/// Product service. The product service is used to manage the products in the
|
||||||
/// a shopping cart can contain. Each product must extend the [Product] class.
|
/// shopping cart.
|
||||||
/// The service is used to add, remove, and update products.
|
final ShoppingCartService service;
|
||||||
///
|
|
||||||
/// The service can be seperate for each shopping cart in-case you want to
|
|
||||||
/// support seperate shopping carts for shop.
|
|
||||||
ProductService<T> productService = ProductService<T>(<T>[]);
|
|
||||||
|
|
||||||
/// Product item builder. This builder is used to build the product item
|
/// Product item builder. This builder is used to build the product item
|
||||||
/// that will be displayed in the shopping cart.
|
/// that will be displayed in the shopping cart.
|
||||||
final Widget Function(
|
final Widget Function(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
Locale locale,
|
|
||||||
Product product,
|
Product product,
|
||||||
ProductService<Product> productService,
|
|
||||||
ShoppingCartConfig configuration,
|
ShoppingCartConfig configuration,
|
||||||
) productItemBuilder;
|
) productItemBuilder;
|
||||||
|
|
||||||
final Widget Function(BuildContext context) _noContentBuilder;
|
|
||||||
|
|
||||||
/// No content builder. This builder is used to build the no content widget
|
|
||||||
/// that will be displayed in the shopping cart when there are no products.
|
|
||||||
Widget Function(BuildContext context) get noContentBuilder =>
|
|
||||||
_noContentBuilder;
|
|
||||||
|
|
||||||
/// Confirm order button builder. This builder is used to build the confirm
|
/// Confirm order button builder. This builder is used to build the confirm
|
||||||
/// order button that will be displayed in the shopping cart.
|
/// order button that will be displayed in the shopping cart.
|
||||||
/// If you override this builder, you cannot use the [onConfirmOrder] callback
|
/// If you override this builder, you cannot use the [onConfirmOrder] callback
|
||||||
final Widget Function(BuildContext context)? confirmOrderButtonBuilder;
|
final Widget Function(
|
||||||
|
BuildContext context,
|
||||||
|
ShoppingCartConfig configuration,
|
||||||
|
Function(List<Product> products) onConfirmOrder,
|
||||||
|
) confirmOrderButtonBuilder;
|
||||||
|
|
||||||
/// Confirm order button height. The height of the confirm order button.
|
/// Confirm order button height. The height of the confirm order button.
|
||||||
/// This height is used to calculate the bottom padding of the shopping cart.
|
/// This height is used to calculate the bottom padding of the shopping cart.
|
||||||
|
@ -78,12 +53,13 @@ you must use the onConfirmOrder callback.""",
|
||||||
/// Confirm order callback. This callback is called when the confirm order
|
/// Confirm order callback. This callback is called when the confirm order
|
||||||
/// button is pressed. The callback will not be called if you override the
|
/// button is pressed. The callback will not be called if you override the
|
||||||
/// confirm order button builder.
|
/// confirm order button builder.
|
||||||
final Function(List<T> products)? onConfirmOrder;
|
final Function(List<Product> products) onConfirmOrder;
|
||||||
|
|
||||||
/// Sum bottom sheet builder. This builder is used to build the sum bottom
|
/// Sum bottom sheet builder. This builder is used to build the sum bottom
|
||||||
/// sheet that will be displayed in the shopping cart. The sum bottom sheet
|
/// sheet that will be displayed in the shopping cart. The sum bottom sheet
|
||||||
/// can be used to display the total sum of the products in the shopping cart.
|
/// can be used to display the total sum of the products in the shopping cart.
|
||||||
final Widget Function(BuildContext context)? sumBottomSheetBuilder;
|
final Widget Function(BuildContext context, ShoppingCartConfig configuration)
|
||||||
|
sumBottomSheetBuilder;
|
||||||
|
|
||||||
/// Sum bottom sheet height. The height of the sum bottom sheet.
|
/// Sum bottom sheet height. The height of the sum bottom sheet.
|
||||||
/// This height is used to calculate the bottom padding of the shopping cart.
|
/// This height is used to calculate the bottom padding of the shopping cart.
|
||||||
|
@ -92,31 +68,30 @@ you must use the onConfirmOrder callback.""",
|
||||||
|
|
||||||
/// Padding around the shopping cart. The padding is used to create space
|
/// Padding around the shopping cart. The padding is used to create space
|
||||||
/// around the shopping cart.
|
/// around the shopping cart.
|
||||||
final EdgeInsets padding;
|
final EdgeInsets pagePadding;
|
||||||
|
|
||||||
/// Bottom padding of the shopping cart. The bottom padding is used to create
|
/// Bottom padding of the shopping cart. The bottom padding is used to create
|
||||||
/// a padding around the bottom sheet. This padding is ignored when the
|
/// a padding around the bottom sheet. This padding is ignored when the
|
||||||
/// [sumBottomSheetBuilder] is overridden.
|
/// [sumBottomSheetBuilder] is overridden.
|
||||||
final EdgeInsets bottomPadding;
|
final EdgeInsets bottomPadding;
|
||||||
|
|
||||||
/// Title builder. This builder is used to build the title of the shopping
|
/// Title builder. This builder is used to
|
||||||
/// cart. The title is displayed at the top of the shopping cart. If you
|
/// build the title of the shopping cart.
|
||||||
/// use the title builder, the [title] will be ignored.
|
final Widget Function(
|
||||||
final Widget Function(BuildContext context)? titleBuilder;
|
BuildContext context,
|
||||||
|
String title,
|
||||||
|
)? titleBuilder;
|
||||||
|
|
||||||
/// Shopping cart localizations. The localizations are used to localize the
|
/// Shopping cart translations. The translations for the shopping cart.
|
||||||
/// shopping cart.
|
final ShoppingCartTranslations translations;
|
||||||
final ShoppingCartLocalizations localizations;
|
|
||||||
|
|
||||||
/// App bar for the shopping cart screen.
|
/// Appbar for the shopping cart screen.
|
||||||
final PreferredSizeWidget? appBar;
|
final AppBar Function(BuildContext context) appBar;
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _defaultProductItemBuilder(
|
Widget _defaultProductItemBuilder(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
Locale locale,
|
|
||||||
Product product,
|
Product product,
|
||||||
ProductService<Product> service,
|
|
||||||
ShoppingCartConfig configuration,
|
ShoppingCartConfig configuration,
|
||||||
) {
|
) {
|
||||||
var theme = Theme.of(context);
|
var theme = Theme.of(context);
|
||||||
|
@ -172,7 +147,8 @@ Widget _defaultProductItemBuilder(
|
||||||
Icons.remove,
|
Icons.remove,
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
),
|
),
|
||||||
onPressed: () => service.removeOneProduct(product),
|
onPressed: () =>
|
||||||
|
configuration.service.removeOneProduct(product),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(2),
|
padding: const EdgeInsets.all(2),
|
||||||
|
@ -198,7 +174,7 @@ Widget _defaultProductItemBuilder(
|
||||||
Icons.add,
|
Icons.add,
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
),
|
),
|
||||||
onPressed: () => service.addProduct(product),
|
onPressed: () => configuration.service.addProduct(product),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -207,3 +183,76 @@ Widget _defaultProductItemBuilder(
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _defaultSumBottomSheetBuilder(
|
||||||
|
BuildContext context,
|
||||||
|
ShoppingCartConfig configuration,
|
||||||
|
) {
|
||||||
|
var theme = Theme.of(context);
|
||||||
|
|
||||||
|
var totalPrice = configuration.service.products
|
||||||
|
.map((product) => product.price * product.quantity)
|
||||||
|
.fold(0.0, (a, b) => a + b);
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: configuration.bottomPadding,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
configuration.translations.sum,
|
||||||
|
style: theme.textTheme.titleMedium,
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
Text(
|
||||||
|
"€ ${totalPrice.toStringAsFixed(2)}",
|
||||||
|
style: theme.textTheme.bodyMedium,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _defaultConfirmOrderButton(
|
||||||
|
BuildContext context,
|
||||||
|
ShoppingCartConfig configuration,
|
||||||
|
Function(List<Product> products) onConfirmOrder,
|
||||||
|
) {
|
||||||
|
var theme = Theme.of(context);
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 60),
|
||||||
|
child: SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: FilledButton(
|
||||||
|
onPressed: () => onConfirmOrder(
|
||||||
|
configuration.service.products,
|
||||||
|
),
|
||||||
|
style: theme.filledButtonTheme.style?.copyWith(
|
||||||
|
backgroundColor: WidgetStateProperty.all(
|
||||||
|
theme.colorScheme.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16.0,
|
||||||
|
vertical: 12,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
configuration.translations.placeOrder,
|
||||||
|
style: theme.textTheme.displayLarge,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
AppBar _defaultAppBar(BuildContext context) {
|
||||||
|
var theme = Theme.of(context);
|
||||||
|
return AppBar(
|
||||||
|
title: Text(
|
||||||
|
"Shopping cart",
|
||||||
|
style: theme.textTheme.headlineLarge,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1,24 +1,14 @@
|
||||||
import "package:flutter/material.dart";
|
|
||||||
|
|
||||||
/// Shopping cart localizations
|
/// Shopping cart localizations
|
||||||
class ShoppingCartLocalizations {
|
class ShoppingCartTranslations {
|
||||||
/// Creates shopping cart localizations
|
/// Creates shopping cart localizations
|
||||||
const ShoppingCartLocalizations({
|
const ShoppingCartTranslations({
|
||||||
this.locale = const Locale("en", "US"),
|
|
||||||
this.placeOrder = "Order",
|
this.placeOrder = "Order",
|
||||||
this.sum = "Subtotal:",
|
this.sum = "Subtotal:",
|
||||||
this.cartTitle = "Products",
|
this.cartTitle = "Products",
|
||||||
this.close = "close",
|
this.close = "close",
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Locale for the shopping cart.
|
/// Text for the place order button.
|
||||||
/// This locale will be used to format the currency.
|
|
||||||
/// Default is English.
|
|
||||||
final Locale locale;
|
|
||||||
|
|
||||||
/// Localization for the place order button.
|
|
||||||
/// This text will only be displayed if you're not using the place order
|
|
||||||
/// button builder.
|
|
||||||
final String placeOrder;
|
final String placeOrder;
|
||||||
|
|
||||||
/// Localization for the sum.
|
/// Localization for the sum.
|
|
@ -1,71 +0,0 @@
|
||||||
import "package:flutter/foundation.dart";
|
|
||||||
import "package:flutter_shopping/flutter_shopping.dart";
|
|
||||||
|
|
||||||
/// Product service. This class is responsible for managing the products.
|
|
||||||
/// The service is used to add, remove, and update products.
|
|
||||||
class ProductService<T extends Product> extends ChangeNotifier {
|
|
||||||
/// Creates a product service.
|
|
||||||
ProductService(this.products);
|
|
||||||
|
|
||||||
/// List of products in the shopping cart.
|
|
||||||
final List<T> products;
|
|
||||||
|
|
||||||
/// Adds a product to the shopping cart.
|
|
||||||
void addProduct(T product) {
|
|
||||||
for (var p in products) {
|
|
||||||
if (p.id == product.id) {
|
|
||||||
p.quantity++;
|
|
||||||
notifyListeners();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
products.add(product);
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes a product from the shopping cart.
|
|
||||||
void removeProduct(T product) {
|
|
||||||
for (var p in products) {
|
|
||||||
if (p.id == product.id) {
|
|
||||||
products.remove(p);
|
|
||||||
notifyListeners();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes one product from the shopping cart.
|
|
||||||
void removeOneProduct(T product) {
|
|
||||||
for (var p in products) {
|
|
||||||
if (p.id == product.id) {
|
|
||||||
if (p.quantity > 1) {
|
|
||||||
p.quantity--;
|
|
||||||
notifyListeners();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
products.remove(product);
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Counts the number of products in the shopping cart.
|
|
||||||
int countProducts() {
|
|
||||||
var count = 0;
|
|
||||||
|
|
||||||
for (var product in products) {
|
|
||||||
count += product.quantity;
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Empties the shopping cart.
|
|
||||||
void clear() {
|
|
||||||
products.clear();
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,6 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter_shopping/flutter_shopping.dart";
|
import "package:flutter_shopping_cart/flutter_shopping_cart.dart";
|
||||||
|
import "package:flutter_shopping_interface/flutter_shopping_interface.dart";
|
||||||
|
|
||||||
/// A popup that displays the product item.
|
/// A popup that displays the product item.
|
||||||
class ProductItemPopup extends StatelessWidget {
|
class ProductItemPopup extends StatelessWidget {
|
||||||
|
@ -49,7 +50,7 @@ class ProductItemPopup extends StatelessWidget {
|
||||||
vertical: 8.0,
|
vertical: 8.0,
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
configuration.localizations.close,
|
configuration.translations.close,
|
||||||
style: theme.textTheme.displayLarge,
|
style: theme.textTheme.displayLarge,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter_shopping/flutter_shopping.dart";
|
import "package:flutter_shopping_cart/flutter_shopping_cart.dart";
|
||||||
|
|
||||||
/// Shopping cart screen widget.
|
/// Shopping cart screen widget.
|
||||||
class ShoppingCartScreen<T extends Product> extends StatelessWidget {
|
class ShoppingCartScreen extends StatelessWidget {
|
||||||
/// Creates a shopping cart screen.
|
/// Creates a shopping cart screen.
|
||||||
const ShoppingCartScreen({
|
const ShoppingCartScreen({
|
||||||
required this.configuration,
|
required this.configuration,
|
||||||
|
@ -10,7 +10,7 @@ class ShoppingCartScreen<T extends Product> extends StatelessWidget {
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Configuration for the shopping cart screen.
|
/// Configuration for the shopping cart screen.
|
||||||
final ShoppingCartConfig<T> configuration;
|
final ShoppingCartConfig configuration;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -20,7 +20,10 @@ class ShoppingCartScreen<T extends Product> extends StatelessWidget {
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
if (configuration.titleBuilder != null) ...{
|
if (configuration.titleBuilder != null) ...{
|
||||||
configuration.titleBuilder!(context),
|
configuration.titleBuilder!(
|
||||||
|
context,
|
||||||
|
configuration.translations.cartTitle,
|
||||||
|
),
|
||||||
} else ...{
|
} else ...{
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
|
@ -29,7 +32,7 @@ class ShoppingCartScreen<T extends Product> extends StatelessWidget {
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
configuration.localizations.cartTitle,
|
configuration.translations.cartTitle,
|
||||||
style: theme.textTheme.titleLarge,
|
style: theme.textTheme.titleLarge,
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
),
|
),
|
||||||
|
@ -38,22 +41,16 @@ class ShoppingCartScreen<T extends Product> extends StatelessWidget {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
ListenableBuilder(
|
ListenableBuilder(
|
||||||
listenable: configuration.productService,
|
listenable: configuration.service,
|
||||||
builder: (context, _) {
|
builder: (context, _) {
|
||||||
var products = configuration.productService.products;
|
var products = configuration.service.products;
|
||||||
|
|
||||||
if (products.isEmpty) {
|
|
||||||
return configuration.noContentBuilder(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
for (var product in products)
|
for (var product in products)
|
||||||
configuration.productItemBuilder(
|
configuration.productItemBuilder(
|
||||||
context,
|
context,
|
||||||
configuration.localizations.locale,
|
|
||||||
product,
|
product,
|
||||||
configuration.productService,
|
|
||||||
configuration,
|
configuration,
|
||||||
),
|
),
|
||||||
// Additional whitespace at the bottom to make sure the
|
// Additional whitespace at the bottom to make sure the
|
||||||
|
@ -71,24 +68,18 @@ class ShoppingCartScreen<T extends Product> extends StatelessWidget {
|
||||||
);
|
);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: configuration.appBar ??
|
appBar: configuration.appBar.call(context),
|
||||||
AppBar(
|
|
||||||
title: Text(
|
|
||||||
"Shopping cart",
|
|
||||||
style: theme.textTheme.headlineLarge,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Stack(
|
child: Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: configuration.padding,
|
padding: configuration.pagePadding,
|
||||||
child: productBuilder,
|
child: productBuilder,
|
||||||
),
|
),
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
child: _BottomSheet<T>(
|
child: _BottomSheet(
|
||||||
configuration: configuration,
|
configuration: configuration,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -99,124 +90,31 @@ class ShoppingCartScreen<T extends Product> extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _BottomSheet<T extends Product> extends StatelessWidget {
|
class _BottomSheet extends StatelessWidget {
|
||||||
const _BottomSheet({
|
const _BottomSheet({
|
||||||
required this.configuration,
|
required this.configuration,
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
final ShoppingCartConfig<T> configuration;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
var placeOrderButton = ListenableBuilder(
|
|
||||||
listenable: configuration.productService,
|
|
||||||
builder: (BuildContext context, Widget? child) =>
|
|
||||||
configuration.confirmOrderButtonBuilder != null
|
|
||||||
? configuration.confirmOrderButtonBuilder!(context)
|
|
||||||
: _DefaultConfirmOrderButton<T>(configuration: configuration),
|
|
||||||
);
|
|
||||||
|
|
||||||
var bottomSheet = ListenableBuilder(
|
|
||||||
listenable: configuration.productService,
|
|
||||||
builder: (BuildContext context, Widget? child) =>
|
|
||||||
configuration.sumBottomSheetBuilder != null
|
|
||||||
? configuration.sumBottomSheetBuilder!(context)
|
|
||||||
: _DefaultSumBottomSheet(configuration: configuration),
|
|
||||||
);
|
|
||||||
|
|
||||||
return Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
bottomSheet,
|
|
||||||
placeOrderButton,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _DefaultConfirmOrderButton<T extends Product> extends StatelessWidget {
|
|
||||||
const _DefaultConfirmOrderButton({
|
|
||||||
required this.configuration,
|
|
||||||
});
|
|
||||||
|
|
||||||
final ShoppingCartConfig<T> configuration;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
var theme = Theme.of(context);
|
|
||||||
|
|
||||||
void onConfirmOrderPressed(List<T> products) {
|
|
||||||
if (configuration.onConfirmOrder == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (products.isEmpty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
configuration.onConfirmOrder!(products);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 60),
|
|
||||||
child: SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
child: FilledButton(
|
|
||||||
onPressed: () => onConfirmOrderPressed(
|
|
||||||
configuration.productService.products,
|
|
||||||
),
|
|
||||||
style: theme.filledButtonTheme.style?.copyWith(
|
|
||||||
backgroundColor: WidgetStateProperty.all(
|
|
||||||
theme.colorScheme.primary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 16.0,
|
|
||||||
vertical: 12,
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
configuration.localizations.placeOrder,
|
|
||||||
style: theme.textTheme.displayLarge,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _DefaultSumBottomSheet extends StatelessWidget {
|
|
||||||
const _DefaultSumBottomSheet({
|
|
||||||
required this.configuration,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final ShoppingCartConfig configuration;
|
final ShoppingCartConfig configuration;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) => Column(
|
||||||
var theme = Theme.of(context);
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
|
||||||
var totalPrice = configuration.productService.products
|
|
||||||
.map((product) => product.price * product.quantity)
|
|
||||||
.fold(0.0, (a, b) => a + b);
|
|
||||||
|
|
||||||
return Padding(
|
|
||||||
padding: configuration.bottomPadding,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
children: [
|
||||||
Text(
|
ListenableBuilder(
|
||||||
configuration.localizations.sum,
|
listenable: configuration.service,
|
||||||
style: theme.textTheme.titleMedium,
|
builder: (BuildContext context, Widget? child) =>
|
||||||
|
configuration.sumBottomSheetBuilder(context, configuration),
|
||||||
|
),
|
||||||
|
ListenableBuilder(
|
||||||
|
listenable: configuration.service,
|
||||||
|
builder: (BuildContext context, Widget? child) =>
|
||||||
|
configuration.confirmOrderButtonBuilder(
|
||||||
|
context,
|
||||||
|
configuration,
|
||||||
|
configuration.onConfirmOrder,
|
||||||
),
|
),
|
||||||
const Spacer(),
|
|
||||||
Text(
|
|
||||||
"€ ${totalPrice.toStringAsFixed(2)}",
|
|
||||||
style: theme.textTheme.bodyMedium,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,23 @@
|
||||||
name: flutter_shopping_cart
|
name: flutter_shopping_cart
|
||||||
description: "A Flutter module for a shopping cart."
|
description: "A Flutter module for a shopping cart."
|
||||||
version: 2.0.0
|
version: 2.0.0
|
||||||
publish_to: 'none'
|
publish_to: "none"
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.3.0 <4.0.0'
|
sdk: ">=3.3.0 <4.0.0"
|
||||||
flutter: ">=1.17.0"
|
flutter: ">=1.17.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_shopping:
|
flutter_shopping_interface:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/Iconica-Development/flutter_shopping
|
url: https://github.com/Iconica-Development/flutter_shopping
|
||||||
path: packages/flutter_shopping
|
path: packages/flutter_shopping_interface
|
||||||
ref: 2.0.0
|
ref: 2.0.0
|
||||||
|
dependency_overrides:
|
||||||
|
flutter_shopping_interface:
|
||||||
|
path: ../flutter_shopping_interface
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
@ -17,4 +17,7 @@ abstract class ShoppingCartService with ChangeNotifier {
|
||||||
|
|
||||||
/// Clears the shopping cart.
|
/// Clears the shopping cart.
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
/// The list of products in the shopping cart.
|
||||||
|
List<Product> get products;
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,4 +54,7 @@ class LocalShoppingCartService
|
||||||
}
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Product> get products => _products;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue