mirror of
https://github.com/Iconica-Development/flutter_shopping.git
synced 2025-05-19 17:03:45 +02:00
feat: add userstory
This commit is contained in:
parent
ee22dc98e6
commit
903550659a
50 changed files with 583 additions and 1623 deletions
|
@ -1,6 +1,8 @@
|
||||||
/// Flutter component for shopping cart.
|
/// Flutter component for shopping cart.
|
||||||
library flutter_order_details;
|
library flutter_order_details;
|
||||||
|
|
||||||
|
export "package:flutter_form_wizard/flutter_form.dart";
|
||||||
|
|
||||||
export "src/configuration/order_detail_configuration.dart";
|
export "src/configuration/order_detail_configuration.dart";
|
||||||
export "src/configuration/order_detail_translations.dart";
|
export "src/configuration/order_detail_translations.dart";
|
||||||
export "src/order_detail_screen.dart";
|
export "src/order_detail_screen.dart";
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
import "package:animated_toggle/animated_toggle.dart";
|
import "package:animated_toggle/animated_toggle.dart";
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter_form_wizard/flutter_form.dart";
|
|
||||||
import "package:flutter_order_details/flutter_order_details.dart";
|
import "package:flutter_order_details/flutter_order_details.dart";
|
||||||
import "package:flutter_shopping_interface/flutter_shopping_interface.dart";
|
import "package:flutter_shopping_interface/flutter_shopping_interface.dart";
|
||||||
|
|
||||||
|
@ -14,19 +13,25 @@ class OrderDetailConfiguration {
|
||||||
required this.onNextStep,
|
required this.onNextStep,
|
||||||
required this.onStepsCompleted,
|
required this.onStepsCompleted,
|
||||||
required this.onCompleteOrderDetails,
|
required this.onCompleteOrderDetails,
|
||||||
this.pages = _defaultPages,
|
this.pages,
|
||||||
this.translations = const OrderDetailTranslations(),
|
this.translations,
|
||||||
this.appBar = _defaultAppBar,
|
this.appBar,
|
||||||
this.nextbuttonBuilder = _defaultNextButtonBuilder,
|
this.nextbuttonBuilder,
|
||||||
this.orderSuccessBuilder = _defaultOrderSuccess,
|
this.orderSuccessBuilder,
|
||||||
});
|
}) {
|
||||||
|
pages ??= _defaultPages;
|
||||||
|
translations ??= const OrderDetailTranslations();
|
||||||
|
appBar ??= _defaultAppBar;
|
||||||
|
nextbuttonBuilder ??= _defaultNextButtonBuilder;
|
||||||
|
orderSuccessBuilder ??= _defaultOrderSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
/// The shopping service that is used
|
/// The shopping service that is used
|
||||||
final ShoppingService shoppingService;
|
final ShoppingService shoppingService;
|
||||||
|
|
||||||
/// The different steps that the user has to go through to complete the order.
|
/// The different steps that the user has to go through to complete the order.
|
||||||
/// Each step contains a list of fields that the user has to fill in.
|
/// Each step contains a list of fields that the user has to fill in.
|
||||||
final List<FlutterFormPage> Function(BuildContext context) pages;
|
List<FlutterFormPage> Function(BuildContext context)? pages;
|
||||||
|
|
||||||
/// Callback function that is called when the user has completed the order.
|
/// Callback function that is called when the user has completed the order.
|
||||||
/// The result of the order is passed as an argument to the function.
|
/// The result of the order is passed as an argument to the function.
|
||||||
|
@ -38,33 +43,37 @@ class OrderDetailConfiguration {
|
||||||
) onStepsCompleted;
|
) onStepsCompleted;
|
||||||
|
|
||||||
/// Callback function that is called when the user has completed a step.
|
/// Callback function that is called when the user has completed a step.
|
||||||
final Function(int currentStep, Map<String, dynamic> data) onNextStep;
|
final Function(
|
||||||
|
int currentStep,
|
||||||
|
Map<String, dynamic> data,
|
||||||
|
FlutterFormController controller,
|
||||||
|
) onNextStep;
|
||||||
|
|
||||||
/// Localization for the order detail screen.
|
/// Localization for the order detail screen.
|
||||||
final OrderDetailTranslations translations;
|
OrderDetailTranslations? translations;
|
||||||
|
|
||||||
/// Optional app bar that you can pass to the order detail screen.
|
/// Optional app bar that you can pass to the order detail screen.
|
||||||
final AppBar Function(
|
AppBar Function(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
String title,
|
String title,
|
||||||
) appBar;
|
)? appBar;
|
||||||
|
|
||||||
/// Optional next button builder that you can pass to the order detail screen.
|
/// Optional next button builder that you can pass to the order detail screen.
|
||||||
final Widget Function(
|
Widget Function(
|
||||||
int currentStep,
|
int currentStep,
|
||||||
// ignore: avoid_positional_boolean_parameters
|
// ignore: avoid_positional_boolean_parameters
|
||||||
bool checkingPages,
|
bool checkingPages,
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
OrderDetailConfiguration configuration,
|
OrderDetailConfiguration configuration,
|
||||||
FlutterFormController controller,
|
FlutterFormController controller,
|
||||||
) nextbuttonBuilder;
|
)? nextbuttonBuilder;
|
||||||
|
|
||||||
/// Optional builder for the order success screen.
|
/// Optional builder for the order success screen.
|
||||||
final Widget Function(
|
Widget Function(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
OrderDetailConfiguration,
|
OrderDetailConfiguration,
|
||||||
Map<int, Map<String, dynamic>> orderDetails,
|
Map<int, Map<String, dynamic>> orderDetails,
|
||||||
) orderSuccessBuilder;
|
)? orderSuccessBuilder;
|
||||||
|
|
||||||
/// This function is called after the order has been completed and
|
/// This function is called after the order has been completed and
|
||||||
/// the success screen has been shown.
|
/// the success screen has been shown.
|
||||||
|
@ -107,8 +116,11 @@ Widget _defaultNextButtonBuilder(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: FilledButton(
|
child: FilledButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
controller.validateAndSaveCurrentStep();
|
configuration.onNextStep(
|
||||||
await controller.autoNextStep();
|
currentStep,
|
||||||
|
controller.getCurrentStepResults(),
|
||||||
|
controller,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
style: theme.filledButtonTheme.style?.copyWith(
|
style: theme.filledButtonTheme.style?.copyWith(
|
||||||
backgroundColor: WidgetStateProperty.all(
|
backgroundColor: WidgetStateProperty.all(
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter_form_wizard/flutter_form.dart";
|
|
||||||
import "package:flutter_order_details/flutter_order_details.dart";
|
import "package:flutter_order_details/flutter_order_details.dart";
|
||||||
|
|
||||||
/// Order Detail Screen.
|
/// Order Detail Screen.
|
||||||
|
@ -22,22 +21,22 @@ class _OrderDetailScreenState extends State<OrderDetailScreen> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var controller = FlutterFormController();
|
var controller = FlutterFormController();
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: widget.configuration.appBar.call(
|
appBar: widget.configuration.appBar!.call(
|
||||||
context,
|
context,
|
||||||
widget.configuration.translations.orderDetailsTitle,
|
widget.configuration.translations!.orderDetailsTitle,
|
||||||
),
|
),
|
||||||
body: FlutterForm(
|
body: FlutterForm(
|
||||||
formController: controller,
|
formController: controller,
|
||||||
options: FlutterFormOptions(
|
options: FlutterFormOptions(
|
||||||
nextButton: (pageNumber, checkingPages) =>
|
nextButton: (pageNumber, checkingPages) =>
|
||||||
widget.configuration.nextbuttonBuilder(
|
widget.configuration.nextbuttonBuilder!(
|
||||||
pageNumber,
|
pageNumber,
|
||||||
checkingPages,
|
checkingPages,
|
||||||
context,
|
context,
|
||||||
widget.configuration,
|
widget.configuration,
|
||||||
controller,
|
controller,
|
||||||
),
|
),
|
||||||
pages: widget.configuration.pages.call(context),
|
pages: widget.configuration.pages!.call(context),
|
||||||
onFinished: (data) async {
|
onFinished: (data) async {
|
||||||
widget.configuration.onStepsCompleted.call(
|
widget.configuration.onStepsCompleted.call(
|
||||||
widget.configuration.shoppingService.shopService.selectedShop!.id,
|
widget.configuration.shoppingService.shopService.selectedShop!.id,
|
||||||
|
@ -47,7 +46,7 @@ class _OrderDetailScreenState extends State<OrderDetailScreen> {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onNext: (step, data) {
|
onNext: (step, data) {
|
||||||
widget.configuration.onNextStep.call(step, data);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -24,10 +24,6 @@ dependencies:
|
||||||
ref: 2.0.0
|
ref: 2.0.0
|
||||||
collection: ^1.18.0
|
collection: ^1.18.0
|
||||||
|
|
||||||
dependency_overrides:
|
|
||||||
flutter_shopping_interface:
|
|
||||||
path: ../flutter_shopping_interface
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter_product_page/flutter_product_page.dart";
|
import "package:flutter_product_page/flutter_product_page.dart";
|
||||||
|
import "package:flutter_product_page/src/widgets/product_item.dart";
|
||||||
import "package:flutter_product_page/src/widgets/product_item_popup.dart";
|
import "package:flutter_product_page/src/widgets/product_item_popup.dart";
|
||||||
import "package:flutter_shopping_interface/flutter_shopping_interface.dart";
|
import "package:flutter_shopping_interface/flutter_shopping_interface.dart";
|
||||||
|
|
||||||
|
@ -13,20 +14,31 @@ class ProductPageConfiguration {
|
||||||
required this.onAddToCart,
|
required this.onAddToCart,
|
||||||
required this.onNavigateToShoppingCart,
|
required this.onNavigateToShoppingCart,
|
||||||
required this.getProductsInShoppingCart,
|
required this.getProductsInShoppingCart,
|
||||||
this.shoppingCartButtonBuilder = _defaultShoppingCartButtonBuilder,
|
this.shoppingCartButtonBuilder,
|
||||||
this.initialShopId,
|
this.initialShopId,
|
||||||
this.productBuilder,
|
this.productBuilder,
|
||||||
this.onShopSelectionChange,
|
this.onShopSelectionChange,
|
||||||
this.translations = const ProductPageTranslations(),
|
this.translations,
|
||||||
this.shopSelectorStyle = ShopSelectorStyle.spacedWrap,
|
this.shopSelectorStyle,
|
||||||
this.pagePadding = const EdgeInsets.all(4),
|
this.pagePadding,
|
||||||
this.appBar = _defaultAppBar,
|
this.appBar,
|
||||||
this.bottomNavigationBar,
|
this.bottomNavigationBar,
|
||||||
this.onProductDetail = _onProductDetail,
|
this.onProductDetail,
|
||||||
this.discountDescription = _defaultDiscountDescription,
|
this.discountDescription,
|
||||||
this.noContentBuilder = _defaultNoContentBuilder,
|
this.noContentBuilder,
|
||||||
this.errorBuilder = _defaultErrorBuilder,
|
this.errorBuilder,
|
||||||
});
|
}) {
|
||||||
|
shoppingCartButtonBuilder ??= _defaultShoppingCartButtonBuilder;
|
||||||
|
productBuilder ??= _defaultProductBuilder;
|
||||||
|
appBar ??= _defaultAppBar;
|
||||||
|
onProductDetail ??= _onProductDetail;
|
||||||
|
discountDescription ??= _defaultDiscountDescription;
|
||||||
|
noContentBuilder ??= _defaultNoContentBuilder;
|
||||||
|
errorBuilder ??= _defaultErrorBuilder;
|
||||||
|
translations ??= const ProductPageTranslations();
|
||||||
|
shopSelectorStyle ??= ShopSelectorStyle.row;
|
||||||
|
pagePadding ??= const EdgeInsets.all(4);
|
||||||
|
}
|
||||||
|
|
||||||
/// The shopping service that is used
|
/// The shopping service that is used
|
||||||
final ShoppingService shoppingService;
|
final ShoppingService shoppingService;
|
||||||
|
@ -42,32 +54,36 @@ class ProductPageConfiguration {
|
||||||
final Future<List<Product>> Function(Shop shop) getProducts;
|
final Future<List<Product>> Function(Shop shop) getProducts;
|
||||||
|
|
||||||
/// The localizations for the product page.
|
/// The localizations for the product page.
|
||||||
final ProductPageTranslations translations;
|
ProductPageTranslations? translations;
|
||||||
|
|
||||||
/// Builder for the product item. These items will be displayed in the list
|
/// Builder for the product item. These items will be displayed in the list
|
||||||
/// for each product in their seperated category. This builder should only
|
/// for each product in their seperated category. This builder should only
|
||||||
/// build the widget for one specific product. This builder has a default
|
/// build the widget for one specific product. This builder has a default
|
||||||
/// in-case the developer does not override it.
|
/// in-case the developer does not override it.
|
||||||
Widget Function(BuildContext context, Product product)? productBuilder;
|
Widget Function(
|
||||||
|
BuildContext context,
|
||||||
|
Product product,
|
||||||
|
ProductPageConfiguration configuration,
|
||||||
|
)? productBuilder;
|
||||||
|
|
||||||
/// The builder for the product popup. This builder should return a widget
|
/// The builder for the product popup. This builder should return a widget
|
||||||
Function(
|
Function(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
Product product,
|
Product product,
|
||||||
String closeText,
|
String closeText,
|
||||||
) onProductDetail;
|
)? onProductDetail;
|
||||||
|
|
||||||
/// The builder for the shopping cart. This builder should return a widget
|
/// The builder for the shopping cart. This builder should return a widget
|
||||||
/// that navigates to the shopping cart overview page.
|
/// that navigates to the shopping cart overview page.
|
||||||
Widget Function(
|
Widget Function(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
ProductPageConfiguration configuration,
|
ProductPageConfiguration configuration,
|
||||||
) shoppingCartButtonBuilder;
|
)? shoppingCartButtonBuilder;
|
||||||
|
|
||||||
/// The function that returns the discount description for a product.
|
/// The function that returns the discount description for a product.
|
||||||
String Function(
|
String Function(
|
||||||
Product product,
|
Product product,
|
||||||
) discountDescription;
|
)? discountDescription;
|
||||||
|
|
||||||
/// This function must be implemented by the developer and should handle the
|
/// This function must be implemented by the developer and should handle the
|
||||||
/// adding of a product to the cart.
|
/// adding of a product to the cart.
|
||||||
|
@ -86,20 +102,20 @@ class ProductPageConfiguration {
|
||||||
final Function() onNavigateToShoppingCart;
|
final Function() onNavigateToShoppingCart;
|
||||||
|
|
||||||
/// The style of the shop selector.
|
/// The style of the shop selector.
|
||||||
final ShopSelectorStyle shopSelectorStyle;
|
ShopSelectorStyle? shopSelectorStyle;
|
||||||
|
|
||||||
/// The padding for the page.
|
/// The padding for the page.
|
||||||
final EdgeInsets pagePadding;
|
EdgeInsets? pagePadding;
|
||||||
|
|
||||||
/// Optional app bar that you can pass to the product page screen.
|
/// Optional app bar that you can pass to the product page screen.
|
||||||
final Widget? bottomNavigationBar;
|
final Widget? bottomNavigationBar;
|
||||||
|
|
||||||
/// Optional app bar that you can pass to the order detail screen.
|
/// Optional app bar that you can pass to the order detail screen.
|
||||||
final AppBar Function(BuildContext context)? appBar;
|
AppBar Function(BuildContext context)? appBar;
|
||||||
|
|
||||||
/// Builder for the no content widget. This builder is used when there is no
|
/// Builder for the no content widget. This builder is used when there is no
|
||||||
/// content to display.
|
/// content to display.
|
||||||
final Widget Function(
|
Widget Function(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
)? noContentBuilder;
|
)? noContentBuilder;
|
||||||
|
|
||||||
|
@ -109,7 +125,7 @@ class ProductPageConfiguration {
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
Object? error,
|
Object? error,
|
||||||
StackTrace? stackTrace,
|
StackTrace? stackTrace,
|
||||||
) errorBuilder;
|
)? errorBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
AppBar _defaultAppBar(
|
AppBar _defaultAppBar(
|
||||||
|
@ -157,7 +173,7 @@ Widget _defaultShoppingCartButtonBuilder(
|
||||||
vertical: 12,
|
vertical: 12,
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
configuration.translations.navigateToShoppingCart,
|
configuration.translations!.navigateToShoppingCart,
|
||||||
style: theme.textTheme.displayLarge,
|
style: theme.textTheme.displayLarge,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -214,3 +230,15 @@ Widget _defaultErrorBuilder(
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _defaultProductBuilder(
|
||||||
|
BuildContext context,
|
||||||
|
Product product,
|
||||||
|
ProductPageConfiguration configuration,
|
||||||
|
) =>
|
||||||
|
ProductItem(
|
||||||
|
product: product,
|
||||||
|
onProductDetail: configuration.onProductDetail!,
|
||||||
|
onAddToCart: (Product product) => configuration.onAddToCart(product),
|
||||||
|
translations: configuration.translations!,
|
||||||
|
);
|
||||||
|
|
|
@ -23,11 +23,11 @@ class ProductPageScreen extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => Scaffold(
|
Widget build(BuildContext context) => Scaffold(
|
||||||
appBar: configuration.appBar!.call(context),
|
appBar: configuration.appBar?.call(context),
|
||||||
bottomNavigationBar: configuration.bottomNavigationBar,
|
bottomNavigationBar: configuration.bottomNavigationBar,
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: configuration.pagePadding,
|
padding: configuration.pagePadding!,
|
||||||
child: FutureBuilder(
|
child: FutureBuilder(
|
||||||
// ignore: discarded_futures
|
// ignore: discarded_futures
|
||||||
future: configuration.shops(),
|
future: configuration.shops(),
|
||||||
|
@ -43,7 +43,7 @@ class ProductPageScreen extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.hasError) {
|
if (data.hasError) {
|
||||||
return configuration.errorBuilder(
|
return configuration.errorBuilder!(
|
||||||
context,
|
context,
|
||||||
data.error,
|
data.error,
|
||||||
data.stackTrace,
|
data.stackTrace,
|
||||||
|
@ -53,7 +53,7 @@ class ProductPageScreen extends StatelessWidget {
|
||||||
List<Shop>? shops = data.data;
|
List<Shop>? shops = data.data;
|
||||||
|
|
||||||
if (shops == null || shops.isEmpty) {
|
if (shops == null || shops.isEmpty) {
|
||||||
return configuration.errorBuilder(context, null, null);
|
return configuration.errorBuilder!(context, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (initialBuildShopId != null) {
|
if (initialBuildShopId != null) {
|
||||||
|
@ -137,7 +137,7 @@ class _ProductPage extends StatelessWidget {
|
||||||
pageContent,
|
pageContent,
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
child: configuration.shoppingCartButtonBuilder(
|
child: configuration.shoppingCartButtonBuilder!(
|
||||||
context,
|
context,
|
||||||
configuration,
|
configuration,
|
||||||
),
|
),
|
||||||
|
@ -159,7 +159,7 @@ class _ShopContents extends StatelessWidget {
|
||||||
var theme = Theme.of(context);
|
var theme = Theme.of(context);
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.symmetric(
|
padding: EdgeInsets.symmetric(
|
||||||
horizontal: configuration.pagePadding.horizontal,
|
horizontal: configuration.pagePadding!.horizontal,
|
||||||
),
|
),
|
||||||
child: FutureBuilder(
|
child: FutureBuilder(
|
||||||
// ignore: discarded_futures
|
// ignore: discarded_futures
|
||||||
|
@ -172,7 +172,7 @@ class _ShopContents extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (snapshot.hasError) {
|
if (snapshot.hasError) {
|
||||||
return configuration.errorBuilder(
|
return configuration.errorBuilder!(
|
||||||
context,
|
context,
|
||||||
snapshot.error,
|
snapshot.error,
|
||||||
snapshot.stackTrace,
|
snapshot.stackTrace,
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter_nested_categories/flutter_nested_categories.dart";
|
import "package:flutter_nested_categories/flutter_nested_categories.dart";
|
||||||
import "package:flutter_product_page/flutter_product_page.dart";
|
import "package:flutter_product_page/flutter_product_page.dart";
|
||||||
import "package:flutter_product_page/src/widgets/product_item.dart";
|
|
||||||
import "package:flutter_shopping_interface/flutter_shopping_interface.dart";
|
import "package:flutter_shopping_interface/flutter_shopping_interface.dart";
|
||||||
|
|
||||||
/// Generates a [CategoryList] from a list of [Product]s and a
|
/// Generates a [CategoryList] from a list of [Product]s and a
|
||||||
|
@ -25,15 +24,8 @@ Widget getCategoryList(
|
||||||
categorizedProducts.forEach((categoryName, productList) {
|
categorizedProducts.forEach((categoryName, productList) {
|
||||||
var productWidgets = productList
|
var productWidgets = productList
|
||||||
.map(
|
.map(
|
||||||
(product) => configuration.productBuilder != null
|
(product) =>
|
||||||
? configuration.productBuilder!(context, product)
|
configuration.productBuilder!(context, product, configuration),
|
||||||
: ProductItem(
|
|
||||||
product: product,
|
|
||||||
onProductDetail: configuration.onProductDetail,
|
|
||||||
onAddToCart: (Product product) =>
|
|
||||||
configuration.onAddToCart(product),
|
|
||||||
translations: configuration.translations,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.toList();
|
.toList();
|
||||||
var category = Category(
|
var category = Category(
|
||||||
|
|
|
@ -28,7 +28,7 @@ class WeeklyDiscount extends StatelessWidget {
|
||||||
var bottomText = Padding(
|
var bottomText = Padding(
|
||||||
padding: const EdgeInsets.all(20.0),
|
padding: const EdgeInsets.all(20.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
configuration.discountDescription(product),
|
configuration.discountDescription!(product),
|
||||||
style: theme.textTheme.bodyMedium,
|
style: theme.textTheme.bodyMedium,
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
),
|
),
|
||||||
|
@ -51,7 +51,7 @@ class WeeklyDiscount extends StatelessWidget {
|
||||||
Icons.error_outline_rounded,
|
Icons.error_outline_rounded,
|
||||||
color: Colors.red,
|
color: Colors.red,
|
||||||
),
|
),
|
||||||
Text(configuration.translations.failedToLoadImageExplenation),
|
Text(configuration.translations!.failedToLoadImageExplenation),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -87,7 +87,7 @@ class WeeklyDiscount extends StatelessWidget {
|
||||||
horizontal: 16,
|
horizontal: 16,
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
configuration.translations.discountTitle,
|
configuration.translations!.discountTitle,
|
||||||
style: theme.textTheme.headlineSmall,
|
style: theme.textTheme.headlineSmall,
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
),
|
),
|
||||||
|
|
|
@ -23,10 +23,6 @@ dependencies:
|
||||||
collection: ^1.18.0
|
collection: ^1.18.0
|
||||||
provider: ^6.1.2
|
provider: ^6.1.2
|
||||||
|
|
||||||
dependency_overrides:
|
|
||||||
flutter_shopping_interface:
|
|
||||||
path: ../flutter_shopping_interface
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
53
packages/flutter_shopping/example/.gitignore
vendored
53
packages/flutter_shopping/example/.gitignore
vendored
|
@ -1,53 +0,0 @@
|
||||||
# Miscellaneous
|
|
||||||
*.class
|
|
||||||
*.log
|
|
||||||
*.pyc
|
|
||||||
*.swp
|
|
||||||
.DS_Store
|
|
||||||
.atom/
|
|
||||||
.buildlog/
|
|
||||||
.history
|
|
||||||
.svn/
|
|
||||||
migrate_working_dir/
|
|
||||||
|
|
||||||
# IntelliJ related
|
|
||||||
*.iml
|
|
||||||
*.ipr
|
|
||||||
*.iws
|
|
||||||
.idea/
|
|
||||||
|
|
||||||
# The .vscode folder contains launch configuration and tasks you configure in
|
|
||||||
# VS Code which you may wish to be included in version control, so this line
|
|
||||||
# is commented out by default.
|
|
||||||
.vscode/
|
|
||||||
|
|
||||||
# Flutter/Dart/Pub related
|
|
||||||
**/doc/api/
|
|
||||||
**/ios/Flutter/.last_build_id
|
|
||||||
.dart_tool/
|
|
||||||
.flutter-plugins
|
|
||||||
.flutter-plugins-dependencies
|
|
||||||
.pub-cache/
|
|
||||||
.pub/
|
|
||||||
/build/
|
|
||||||
.metadata
|
|
||||||
pubspec.lock
|
|
||||||
|
|
||||||
# Symbolication related
|
|
||||||
app.*.symbols
|
|
||||||
|
|
||||||
# Obfuscation related
|
|
||||||
app.*.map.json
|
|
||||||
|
|
||||||
# Android Studio will place build artifacts here
|
|
||||||
/android/app/debug
|
|
||||||
/android/app/profile
|
|
||||||
/android/app/release
|
|
||||||
|
|
||||||
# Platforms
|
|
||||||
android/
|
|
||||||
ios/
|
|
||||||
linux/
|
|
||||||
macos/
|
|
||||||
web/
|
|
||||||
windows/
|
|
|
@ -1,16 +0,0 @@
|
||||||
# example
|
|
||||||
|
|
||||||
A new Flutter project.
|
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
|
|
||||||
This project is a starting point for a Flutter application.
|
|
||||||
|
|
||||||
A few resources to get you started if this is your first Flutter project:
|
|
||||||
|
|
||||||
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
|
|
||||||
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
|
|
||||||
|
|
||||||
For help getting started with Flutter development, view the
|
|
||||||
[online documentation](https://docs.flutter.dev/), which offers tutorials,
|
|
||||||
samples, guidance on mobile development, and a full API reference.
|
|
|
@ -1,7 +0,0 @@
|
||||||
include: package:flutter_iconica_analysis/analysis_options.yaml
|
|
||||||
|
|
||||||
analyzer:
|
|
||||||
exclude:
|
|
||||||
|
|
||||||
linter:
|
|
||||||
rules:
|
|
|
@ -1,22 +0,0 @@
|
||||||
import "package:example/src/routes.dart";
|
|
||||||
import "package:example/src/utils/theme.dart";
|
|
||||||
import "package:flutter/material.dart";
|
|
||||||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
runApp(const ProviderScope(child: MyApp()));
|
|
||||||
}
|
|
||||||
|
|
||||||
class MyApp extends HookConsumerWidget {
|
|
||||||
const MyApp({
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, WidgetRef ref) => MaterialApp.router(
|
|
||||||
debugShowCheckedModeBanner: false,
|
|
||||||
restorationScopeId: "app",
|
|
||||||
theme: getTheme(),
|
|
||||||
routerConfig: ref.read(routerProvider),
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,189 +0,0 @@
|
||||||
import "package:example/src/routes.dart";
|
|
||||||
import "package:example/src/services/order_service.dart";
|
|
||||||
import "package:example/src/services/shop_service.dart";
|
|
||||||
import "package:flutter/material.dart";
|
|
||||||
import "package:flutter_shopping/flutter_shopping.dart";
|
|
||||||
import "package:go_router/go_router.dart";
|
|
||||||
|
|
||||||
// (REQUIRED): Create your own instance of the ProductService.
|
|
||||||
final ProductService<Product> productService = ProductService([]);
|
|
||||||
|
|
||||||
FlutterShoppingConfiguration getFlutterShoppingConfiguration() =>
|
|
||||||
FlutterShoppingConfiguration(
|
|
||||||
// (REQUIRED): Shop builder configuration
|
|
||||||
shopBuilder: (
|
|
||||||
BuildContext context,
|
|
||||||
String? initialBuildShopId,
|
|
||||||
String? streetName,
|
|
||||||
) =>
|
|
||||||
ProductPageScreen(
|
|
||||||
configuration: ProductPageConfiguration(
|
|
||||||
// (REQUIRED): List of shops that should be displayed
|
|
||||||
// If there is only one, make a list with just one shop.
|
|
||||||
shops: Future.value(getShops()),
|
|
||||||
|
|
||||||
// (REQUIRED): Function to add a product to the cart
|
|
||||||
onAddToCart: productService.addProduct,
|
|
||||||
|
|
||||||
// (REQUIRED): Function to get the products for a shop
|
|
||||||
getProducts: (ProductPageShop shop) =>
|
|
||||||
Future<ProductPageContent>.value(
|
|
||||||
getShopContent(shop.id),
|
|
||||||
),
|
|
||||||
|
|
||||||
// (REQUIRED): Function to navigate to the shopping cart
|
|
||||||
onNavigateToShoppingCart: () async => onCompleteProductPage(context),
|
|
||||||
|
|
||||||
// (RECOMMENDED): Function to get the number of products in the
|
|
||||||
// shopping cart. This is used to display the number of products
|
|
||||||
// in the shopping cart on the product page.
|
|
||||||
getProductsInShoppingCart: productService.countProducts,
|
|
||||||
|
|
||||||
// (RECOMMENDED) Function that returns the description for a
|
|
||||||
// product that is on sale.
|
|
||||||
getDiscountDescription: (product) =>
|
|
||||||
"""${product.name} for just \$${product.discountPrice?.toStringAsFixed(2)}""",
|
|
||||||
|
|
||||||
// (RECOMMENDED) Function that is fired when the shop selection
|
|
||||||
// changes. You could use this to clear your shopping cart or to
|
|
||||||
// change the products so they belong to the correct shop again.
|
|
||||||
onShopSelectionChange: (ProductPageShop shop) =>
|
|
||||||
productService.clear(),
|
|
||||||
|
|
||||||
// (RECOMMENDED) The shop that is initially selected.
|
|
||||||
// Must be one of the shops in the [shops] list.
|
|
||||||
initialShopId: getShops().first.id,
|
|
||||||
|
|
||||||
// (RECOMMENDED) Localizations for the product page.
|
|
||||||
localizations: const ProductPageLocalization(),
|
|
||||||
|
|
||||||
// (OPTIONAL) Appbar
|
|
||||||
appBar: (context) => AppBar(
|
|
||||||
title: const Text("Shop"),
|
|
||||||
leading: IconButton(
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.arrow_back,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
context.go(homePage);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// (OPTIONAL): Initial build shop id that overrides the initialShop
|
|
||||||
initialBuildShopId: initialBuildShopId,
|
|
||||||
),
|
|
||||||
|
|
||||||
// (REQUIRED): Shopping cart builder configuration
|
|
||||||
shoppingCartBuilder: (BuildContext context) => ShoppingCartScreen(
|
|
||||||
configuration: ShoppingCartConfig(
|
|
||||||
// (REQUIRED) product service instance:
|
|
||||||
productService: productService,
|
|
||||||
|
|
||||||
// (REQUIRED) product item builder:
|
|
||||||
productItemBuilder: (context, locale, product, service, config) =>
|
|
||||||
ListTile(
|
|
||||||
title: Text(product.name),
|
|
||||||
subtitle: Text(product.price.toStringAsFixed(2)),
|
|
||||||
leading: Image.network(
|
|
||||||
product.imageUrl,
|
|
||||||
errorBuilder: (context, error, stackTrace) => const Tooltip(
|
|
||||||
message: "Error loading image",
|
|
||||||
child: Icon(
|
|
||||||
Icons.error,
|
|
||||||
color: Colors.red,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
trailing: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.remove),
|
|
||||||
onPressed: () => productService.removeOneProduct(product),
|
|
||||||
),
|
|
||||||
Text("${product.quantity}"),
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.add),
|
|
||||||
onPressed: () => productService.addProduct(product),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// (OPTIONAL/REQUIRED) on confirm order callback:
|
|
||||||
// Either use this callback or the placeOrderButtonBuilder.
|
|
||||||
onConfirmOrder: (products) async => onCompleteShoppingCart(context),
|
|
||||||
|
|
||||||
// (RECOMMENDED) localizations:
|
|
||||||
localizations: const ShoppingCartLocalizations(),
|
|
||||||
|
|
||||||
/// (OPTIONAL) no content builder for when there are no products
|
|
||||||
/// in the shopping cart.
|
|
||||||
noContentBuilder: (context) => const Center(
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.symmetric(vertical: 128),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
Icons.warning,
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
height: 16,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
"Geen producten in winkelmandje",
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// (OPTIONAL) custom appbar:
|
|
||||||
appBar: AppBar(
|
|
||||||
title: const Text("Shopping Cart"),
|
|
||||||
leading: IconButton(
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.arrow_back,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
context.go(FlutterShoppingPathRoutes.shop);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// (REQUIRED): Configuration on what to do when the user story is
|
|
||||||
// completed.
|
|
||||||
onCompleteUserStory: (BuildContext context) {
|
|
||||||
context.go(homePage);
|
|
||||||
},
|
|
||||||
|
|
||||||
// (RECOMMENDED) Handle processing of the order details. This function
|
|
||||||
// should return true if the order was processed successfully, otherwise
|
|
||||||
// false.
|
|
||||||
//
|
|
||||||
// If this function is not provided, it is assumed that the order is
|
|
||||||
// always processed successfully.
|
|
||||||
//
|
|
||||||
// Example use cases that could be implemented here:
|
|
||||||
// - Sending and storing the order on a server,
|
|
||||||
// - Processing payment (if the user decides to pay upfront).
|
|
||||||
// - And many more...
|
|
||||||
onCompleteOrderDetails:
|
|
||||||
(BuildContext context, OrderResult orderDetails) async {
|
|
||||||
if (orderDetails.order["payment_option"] == "Pay now") {
|
|
||||||
// Make the user pay upfront.
|
|
||||||
}
|
|
||||||
|
|
||||||
// If all went well, we can store the order in the database.
|
|
||||||
// Make sure to register whether or not the order was paid.
|
|
||||||
storeOrderInDatabase(productService.products, orderDetails);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
);
|
|
|
@ -1,8 +0,0 @@
|
||||||
import "package:flutter_shopping/flutter_shopping.dart";
|
|
||||||
|
|
||||||
class MyShop extends ProductPageShop {
|
|
||||||
const MyShop({
|
|
||||||
required super.id,
|
|
||||||
required super.name,
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
import "package:example/src/configuration/configuration.dart";
|
|
||||||
import "package:example/src/ui/homepage.dart";
|
|
||||||
import "package:example/src/utils/go_router.dart";
|
|
||||||
import "package:flutter_shopping/flutter_shopping.dart";
|
|
||||||
import "package:go_router/go_router.dart";
|
|
||||||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
|
||||||
|
|
||||||
const String homePage = "/";
|
|
||||||
|
|
||||||
final routerProvider = Provider<GoRouter>(
|
|
||||||
(ref) => GoRouter(
|
|
||||||
initialLocation: homePage,
|
|
||||||
routes: [
|
|
||||||
// Flutter Shopping Story Routes
|
|
||||||
...getShoppingStoryRoutes(
|
|
||||||
configuration: getFlutterShoppingConfiguration(),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Home Route
|
|
||||||
GoRoute(
|
|
||||||
name: "home",
|
|
||||||
path: homePage,
|
|
||||||
pageBuilder: (context, state) => buildScreenWithFadeTransition(
|
|
||||||
context: context,
|
|
||||||
state: state,
|
|
||||||
child: const Homepage(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
|
@ -1,6 +0,0 @@
|
||||||
import "package:flutter_shopping/flutter_shopping.dart";
|
|
||||||
|
|
||||||
/// Example implementation of storing an order in a database.
|
|
||||||
void storeOrderInDatabase(List<Product> products, OrderResult result) {
|
|
||||||
return;
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
import "package:example/src/models/my_shop.dart";
|
|
||||||
import "package:flutter_shopping/flutter_shopping.dart";
|
|
||||||
|
|
||||||
/// This function should have your own implementation. Generally this would
|
|
||||||
/// contain some API call to fetch the list of shops.
|
|
||||||
List<MyShop> getShops() => <MyShop>[
|
|
||||||
const MyShop(id: "1", name: "Shop 1"),
|
|
||||||
const MyShop(id: "2", name: "Shop 2"),
|
|
||||||
const MyShop(id: "3", name: "Shop 3"),
|
|
||||||
];
|
|
||||||
|
|
||||||
ProductPageContent getShopContent(String shopId) {
|
|
||||||
var products = getProducts(shopId);
|
|
||||||
return ProductPageContent(
|
|
||||||
discountedProduct: products.first,
|
|
||||||
products: products,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function should have your own implementation. Generally this would
|
|
||||||
/// contain some API call to fetch the list of products for a shop.
|
|
||||||
List<Product> getProducts(String shopId) => <Product>[
|
|
||||||
Product(
|
|
||||||
id: "1",
|
|
||||||
name: "White bread",
|
|
||||||
price: 2.99,
|
|
||||||
category: "Loaves",
|
|
||||||
imageUrl: "https://via.placeholder.com/150",
|
|
||||||
hasDiscount: true,
|
|
||||||
discountPrice: 1.99,
|
|
||||||
description: "",
|
|
||||||
),
|
|
||||||
Product(
|
|
||||||
id: "2",
|
|
||||||
name: "Brown bread",
|
|
||||||
price: 2.99,
|
|
||||||
category: "Loaves",
|
|
||||||
imageUrl: "https://via.placeholder.com/150",
|
|
||||||
description: "",
|
|
||||||
),
|
|
||||||
Product(
|
|
||||||
id: "3",
|
|
||||||
name: "Cheese sandwich",
|
|
||||||
price: 1.99,
|
|
||||||
category: "Sandwiches",
|
|
||||||
imageUrl: "https://via.placeholder.com/150",
|
|
||||||
description: "",
|
|
||||||
),
|
|
||||||
];
|
|
|
@ -1,20 +0,0 @@
|
||||||
import "package:flutter/material.dart";
|
|
||||||
import "package:flutter_shopping/flutter_shopping.dart";
|
|
||||||
import "package:go_router/go_router.dart";
|
|
||||||
|
|
||||||
class Homepage extends StatelessWidget {
|
|
||||||
const Homepage({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) => Scaffold(
|
|
||||||
body: Center(
|
|
||||||
child: Badge(
|
|
||||||
label: const Text("1"),
|
|
||||||
child: IconButton(
|
|
||||||
icon: const Icon(Icons.shopping_cart_outlined, size: 50),
|
|
||||||
onPressed: () => context.go(FlutterShoppingPathRoutes.shop),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
import "package:flutter/material.dart";
|
|
||||||
import "package:go_router/go_router.dart";
|
|
||||||
|
|
||||||
CustomTransitionPage buildScreenWithFadeTransition<T>({
|
|
||||||
required BuildContext context,
|
|
||||||
required GoRouterState state,
|
|
||||||
required Widget child,
|
|
||||||
}) =>
|
|
||||||
CustomTransitionPage<T>(
|
|
||||||
key: state.pageKey,
|
|
||||||
child: child,
|
|
||||||
transitionsBuilder: (context, animation, secondaryAnimation, child) =>
|
|
||||||
FadeTransition(opacity: animation, child: child),
|
|
||||||
);
|
|
||||||
|
|
||||||
CustomTransitionPage buildScreenWithoutTransition<T>({
|
|
||||||
required BuildContext context,
|
|
||||||
required GoRouterState state,
|
|
||||||
required Widget child,
|
|
||||||
}) =>
|
|
||||||
CustomTransitionPage<T>(
|
|
||||||
key: state.pageKey,
|
|
||||||
child: child,
|
|
||||||
transitionsBuilder: (context, animation, secondaryAnimation, child) =>
|
|
||||||
child,
|
|
||||||
);
|
|
|
@ -1,32 +0,0 @@
|
||||||
import "package:flutter/material.dart";
|
|
||||||
|
|
||||||
ThemeData getTheme() => ThemeData(
|
|
||||||
scaffoldBackgroundColor: const Color.fromRGBO(250, 249, 246, 1),
|
|
||||||
textTheme: const TextTheme(
|
|
||||||
labelMedium: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
titleMedium: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
color: Color.fromRGBO(60, 60, 59, 1),
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
inputDecorationTheme: const InputDecorationTheme(
|
|
||||||
fillColor: Colors.white,
|
|
||||||
),
|
|
||||||
colorScheme: const ColorScheme.light(
|
|
||||||
primary: Color.fromRGBO(64, 87, 122, 1),
|
|
||||||
secondary: Colors.white,
|
|
||||||
surface: Color.fromRGBO(250, 249, 246, 1),
|
|
||||||
),
|
|
||||||
appBarTheme: const AppBarTheme(
|
|
||||||
backgroundColor: Color.fromRGBO(64, 87, 122, 1),
|
|
||||||
titleTextStyle: TextStyle(
|
|
||||||
fontSize: 28,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
|
@ -1,40 +0,0 @@
|
||||||
name: example
|
|
||||||
description: Demonstrates how to use the flutter_shopping package."
|
|
||||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|
||||||
version: 1.0.0
|
|
||||||
|
|
||||||
environment:
|
|
||||||
sdk: '>=3.3.4 <4.0.0'
|
|
||||||
|
|
||||||
dependencies:
|
|
||||||
flutter:
|
|
||||||
sdk: flutter
|
|
||||||
flutter_hooks: ^0.20.0
|
|
||||||
hooks_riverpod: ^2.1.1
|
|
||||||
go_router: 12.1.3
|
|
||||||
flutter_shopping:
|
|
||||||
git:
|
|
||||||
url: https://github.com/Iconica-Development/flutter_shopping
|
|
||||||
path: packages/flutter_shopping
|
|
||||||
ref: 2.0.0
|
|
||||||
|
|
||||||
dev_dependencies:
|
|
||||||
flutter_test:
|
|
||||||
sdk: flutter
|
|
||||||
flutter_iconica_analysis:
|
|
||||||
git:
|
|
||||||
url: https://github.com/Iconica-Development/flutter_iconica_analysis
|
|
||||||
ref: 7.0.0
|
|
||||||
|
|
||||||
flutter:
|
|
||||||
uses-material-design: true
|
|
||||||
# assets:
|
|
||||||
# - images/a_dot_burr.jpeg
|
|
||||||
# - images/a_dot_ham.jpeg
|
|
||||||
|
|
||||||
# fonts:
|
|
||||||
# - family: Schyler
|
|
||||||
# fonts:
|
|
||||||
# - asset: fonts/Schyler-Regular.ttf
|
|
||||||
# - asset: fonts/Schyler-Italic.ttf
|
|
||||||
# style: italic
|
|
|
@ -1,53 +0,0 @@
|
||||||
# Miscellaneous
|
|
||||||
*.class
|
|
||||||
*.log
|
|
||||||
*.pyc
|
|
||||||
*.swp
|
|
||||||
.DS_Store
|
|
||||||
.atom/
|
|
||||||
.buildlog/
|
|
||||||
.history
|
|
||||||
.svn/
|
|
||||||
migrate_working_dir/
|
|
||||||
|
|
||||||
# IntelliJ related
|
|
||||||
*.iml
|
|
||||||
*.ipr
|
|
||||||
*.iws
|
|
||||||
.idea/
|
|
||||||
|
|
||||||
# The .vscode folder contains launch configuration and tasks you configure in
|
|
||||||
# VS Code which you may wish to be included in version control, so this line
|
|
||||||
# is commented out by default.
|
|
||||||
.vscode/
|
|
||||||
|
|
||||||
# Flutter/Dart/Pub related
|
|
||||||
**/doc/api/
|
|
||||||
**/ios/Flutter/.last_build_id
|
|
||||||
.dart_tool/
|
|
||||||
.flutter-plugins
|
|
||||||
.flutter-plugins-dependencies
|
|
||||||
.pub-cache/
|
|
||||||
.pub/
|
|
||||||
/build/
|
|
||||||
.metadata
|
|
||||||
pubspec.lock
|
|
||||||
|
|
||||||
# Symbolication related
|
|
||||||
app.*.symbols
|
|
||||||
|
|
||||||
# Obfuscation related
|
|
||||||
app.*.map.json
|
|
||||||
|
|
||||||
# Android Studio will place build artifacts here
|
|
||||||
/android/app/debug
|
|
||||||
/android/app/profile
|
|
||||||
/android/app/release
|
|
||||||
|
|
||||||
# Platforms
|
|
||||||
/android/
|
|
||||||
/ios/
|
|
||||||
/linux/
|
|
||||||
/macos/
|
|
||||||
/web/
|
|
||||||
/windows/
|
|
|
@ -1,16 +0,0 @@
|
||||||
# amazon
|
|
||||||
|
|
||||||
A new Flutter project.
|
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
|
|
||||||
This project is a starting point for a Flutter application.
|
|
||||||
|
|
||||||
A few resources to get you started if this is your first Flutter project:
|
|
||||||
|
|
||||||
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
|
|
||||||
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
|
|
||||||
|
|
||||||
For help getting started with Flutter development, view the
|
|
||||||
[online documentation](https://docs.flutter.dev/), which offers tutorials,
|
|
||||||
samples, guidance on mobile development, and a full API reference.
|
|
|
@ -1,28 +0,0 @@
|
||||||
# This file configures the analyzer, which statically analyzes Dart code to
|
|
||||||
# check for errors, warnings, and lints.
|
|
||||||
#
|
|
||||||
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
|
||||||
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
|
||||||
# invoked from the command line by running `flutter analyze`.
|
|
||||||
|
|
||||||
# The following line activates a set of recommended lints for Flutter apps,
|
|
||||||
# packages, and plugins designed to encourage good coding practices.
|
|
||||||
include: package:flutter_lints/flutter.yaml
|
|
||||||
|
|
||||||
linter:
|
|
||||||
# The lint rules applied to this project can be customized in the
|
|
||||||
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
|
||||||
# included above or to enable additional rules. A list of all available lints
|
|
||||||
# and their documentation is published at https://dart.dev/lints.
|
|
||||||
#
|
|
||||||
# Instead of disabling a lint rule for the entire project in the
|
|
||||||
# section below, it can also be suppressed for a single line of code
|
|
||||||
# or a specific dart file by using the `// ignore: name_of_lint` and
|
|
||||||
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
|
||||||
# producing the lint.
|
|
||||||
rules:
|
|
||||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
|
||||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
|
||||||
# https://dart.dev/guides/language/analysis-options
|
|
|
@ -1,22 +0,0 @@
|
||||||
import "package:amazon/src/routes.dart";
|
|
||||||
import "package:amazon/src/utils/theme.dart";
|
|
||||||
import "package:flutter/material.dart";
|
|
||||||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
runApp(const ProviderScope(child: MyApp()));
|
|
||||||
}
|
|
||||||
|
|
||||||
class MyApp extends HookConsumerWidget {
|
|
||||||
const MyApp({
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, WidgetRef ref) => MaterialApp.router(
|
|
||||||
debugShowCheckedModeBanner: false,
|
|
||||||
restorationScopeId: "app",
|
|
||||||
theme: getTheme(),
|
|
||||||
routerConfig: ref.read(routerProvider),
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,362 +0,0 @@
|
||||||
import "package:amazon/src/routes.dart";
|
|
||||||
import "package:amazon/src/services/category_service.dart";
|
|
||||||
import "package:flutter/material.dart";
|
|
||||||
import "package:flutter_shopping/flutter_shopping.dart";
|
|
||||||
import "package:go_router/go_router.dart";
|
|
||||||
|
|
||||||
// (REQUIRED): Create your own instance of the ProductService.
|
|
||||||
final ProductService<Product> productService = ProductService([]);
|
|
||||||
|
|
||||||
FlutterShoppingConfiguration getFlutterShoppingConfiguration() =>
|
|
||||||
FlutterShoppingConfiguration(
|
|
||||||
// (REQUIRED): Shop builder configuration
|
|
||||||
shopBuilder: (
|
|
||||||
BuildContext context,
|
|
||||||
String? initialBuildShopId,
|
|
||||||
String? streetName,
|
|
||||||
) {
|
|
||||||
var theme = Theme.of(context);
|
|
||||||
|
|
||||||
return ProductPageScreen(
|
|
||||||
configuration: ProductPageConfiguration(
|
|
||||||
// (REQUIRED): List of shops that should be displayed
|
|
||||||
// If there is only one, make a list with just one shop.
|
|
||||||
shops: Future.value(getCategories()),
|
|
||||||
|
|
||||||
pagePadding: const EdgeInsets.symmetric(horizontal: 0, vertical: 4),
|
|
||||||
|
|
||||||
// (REQUIRED): Function to add a product to the cart
|
|
||||||
onAddToCart: (product) {
|
|
||||||
return productService.addProduct(product);
|
|
||||||
},
|
|
||||||
|
|
||||||
// (REQUIRED): Function to get the products for a shop
|
|
||||||
getProducts: (ProductPageShop shop) =>
|
|
||||||
Future<ProductPageContent>.value(
|
|
||||||
getShopContent(shop.id),
|
|
||||||
),
|
|
||||||
|
|
||||||
// (REQUIRED): Function to navigate to the shopping cart
|
|
||||||
onNavigateToShoppingCart: () => onCompleteProductPage(context),
|
|
||||||
|
|
||||||
shopSelectorStyle: ShopSelectorStyle.row,
|
|
||||||
|
|
||||||
navigateToShoppingCartBuilder: (context, productpageinfo, shop) {
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
},
|
|
||||||
|
|
||||||
bottomNavigationBar: BottomNavigationBar(
|
|
||||||
fixedColor: theme.primaryColor,
|
|
||||||
unselectedItemColor: Colors.black,
|
|
||||||
type: BottomNavigationBarType.fixed,
|
|
||||||
items: const [
|
|
||||||
BottomNavigationBarItem(
|
|
||||||
icon: Icon(Icons.home),
|
|
||||||
label: "Home",
|
|
||||||
),
|
|
||||||
BottomNavigationBarItem(
|
|
||||||
icon: Icon(Icons.person_2_outlined),
|
|
||||||
label: "Profile",
|
|
||||||
),
|
|
||||||
BottomNavigationBarItem(
|
|
||||||
icon: Icon(Icons.shopping_cart_outlined),
|
|
||||||
label: "Cart",
|
|
||||||
),
|
|
||||||
BottomNavigationBarItem(
|
|
||||||
icon: Icon(Icons.menu),
|
|
||||||
label: "Menu",
|
|
||||||
),
|
|
||||||
],
|
|
||||||
showSelectedLabels: false,
|
|
||||||
showUnselectedLabels: false,
|
|
||||||
onTap: (index) {
|
|
||||||
switch (index) {
|
|
||||||
case 0:
|
|
||||||
// context.go(homePage);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
context.go(FlutterShoppingPathRoutes.shoppingCart);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
|
|
||||||
productBuilder: (context, product) => Card(
|
|
||||||
elevation: 0,
|
|
||||||
color: const Color.fromARGB(255, 233, 233, 233),
|
|
||||||
shape: const RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.zero,
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 3,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Image.network(
|
|
||||||
product.imageUrl,
|
|
||||||
loadingBuilder: (context, child, loadingProgress) =>
|
|
||||||
loadingProgress == null
|
|
||||||
? child
|
|
||||||
: const Center(
|
|
||||||
child: CircularProgressIndicator(),
|
|
||||||
),
|
|
||||||
errorBuilder: (context, error, stackTrace) =>
|
|
||||||
const Tooltip(
|
|
||||||
message: "Error loading image",
|
|
||||||
child: Icon(
|
|
||||||
Icons.error,
|
|
||||||
color: Colors.red,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
flex: 5,
|
|
||||||
child: ColoredBox(
|
|
||||||
color: theme.scaffoldBackgroundColor,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(8),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
product.name,
|
|
||||||
style: theme.textTheme.titleMedium,
|
|
||||||
maxLines: 3,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"4.5",
|
|
||||||
style: theme.textTheme.bodyMedium?.copyWith(
|
|
||||||
color: Colors.blue,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Icon(Icons.star, color: Colors.orange),
|
|
||||||
const Icon(Icons.star, color: Colors.orange),
|
|
||||||
const Icon(Icons.star, color: Colors.orange),
|
|
||||||
const Icon(Icons.star, color: Colors.orange),
|
|
||||||
const Icon(Icons.star_half,
|
|
||||||
color: Colors.orange),
|
|
||||||
Text(
|
|
||||||
"(3)",
|
|
||||||
style: theme.textTheme.bodyMedium?.copyWith(
|
|
||||||
color: Colors.grey,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
"\$${product.price.toStringAsFixed(2)}",
|
|
||||||
style: theme.textTheme.titleMedium,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
"Gratis bezorging door Amazon",
|
|
||||||
style: theme.textTheme.bodyMedium?.copyWith(
|
|
||||||
color: Colors.grey,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
FilledButton(
|
|
||||||
onPressed: () {
|
|
||||||
productService.addProduct(product);
|
|
||||||
},
|
|
||||||
child: const Text("In winkelwagen"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// (RECOMMENDED) The shop that is initially selected.
|
|
||||||
// Must be one of the shops in the [shops] list.
|
|
||||||
initialShopId: getCategories().first.id,
|
|
||||||
|
|
||||||
// (RECOMMENDED) Localizations for the product page.
|
|
||||||
localizations: const ProductPageLocalization(),
|
|
||||||
|
|
||||||
noContentBuilder: (context) => Center(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 128),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
const Icon(
|
|
||||||
Icons.warning,
|
|
||||||
size: 48,
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 16,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
"Geen producten gevonden",
|
|
||||||
style: theme.textTheme.titleLarge,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// (OPTIONAL) Appbar
|
|
||||||
appBar: (context) => AppBar(
|
|
||||||
title: const SizedBox(
|
|
||||||
height: 40,
|
|
||||||
child: SearchBar(
|
|
||||||
hintText: "Search products",
|
|
||||||
leading: Icon(
|
|
||||||
Icons.search,
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
trailing: [
|
|
||||||
Icon(
|
|
||||||
Icons.fit_screen_outlined,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
leading: IconButton(
|
|
||||||
icon: const Icon(Icons.arrow_back),
|
|
||||||
onPressed: () {
|
|
||||||
context.go(homePage);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
bottom: AppBar(
|
|
||||||
backgroundColor: const Color.fromRGBO(203, 237, 230, 1),
|
|
||||||
title: Row(
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.location_on_outlined),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
"Bestemming: ${streetName ?? "Mark - 1234AB Doetinchem Nederland"}",
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: theme.textTheme.titleMedium?.copyWith(
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
primary: false,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// (OPTIONAL): Initial build shop id that overrides the initialShop
|
|
||||||
initialBuildShopId: initialBuildShopId,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
// (REQUIRED): Shopping cart builder configuration
|
|
||||||
shoppingCartBuilder: (BuildContext context) => ShoppingCartScreen(
|
|
||||||
configuration: ShoppingCartConfig(
|
|
||||||
// (REQUIRED) product service instance:
|
|
||||||
productService: productService,
|
|
||||||
|
|
||||||
// (REQUIRED) product item builder:
|
|
||||||
productItemBuilder: (context, locale, product, service, config) =>
|
|
||||||
ListTile(
|
|
||||||
title: Text(product.name),
|
|
||||||
subtitle: Text(product.price.toStringAsFixed(2)),
|
|
||||||
leading: Image.network(
|
|
||||||
product.imageUrl,
|
|
||||||
errorBuilder: (context, error, stackTrace) => const Tooltip(
|
|
||||||
message: "Error loading image",
|
|
||||||
child: Icon(
|
|
||||||
Icons.error,
|
|
||||||
color: Colors.red,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
trailing: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.remove),
|
|
||||||
onPressed: () => productService.removeOneProduct(product),
|
|
||||||
),
|
|
||||||
Text("${product.quantity}"),
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.add),
|
|
||||||
onPressed: () => productService.addProduct(product),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// (OPTIONAL/REQUIRED) on confirm order callback:
|
|
||||||
// Either use this callback or the placeOrderButtonBuilder.
|
|
||||||
onConfirmOrder: (products) => onCompleteShoppingCart(context),
|
|
||||||
|
|
||||||
// (RECOMMENDED) localizations:
|
|
||||||
localizations: const ShoppingCartLocalizations(),
|
|
||||||
|
|
||||||
/// (OPTIONAL) no content builder for when there are no products
|
|
||||||
/// in the shopping cart.
|
|
||||||
noContentBuilder: (context) => const Center(
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.symmetric(vertical: 128),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
Icons.warning,
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
height: 16,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
"Geen producten in winkelmandje",
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// (OPTIONAL) custom appbar:
|
|
||||||
appBar: AppBar(
|
|
||||||
title: const Text("Shopping Cart"),
|
|
||||||
leading: IconButton(
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.arrow_back,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
context.go(FlutterShoppingPathRoutes.shop);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// (REQUIRED): Configuration on what to do when the user story is
|
|
||||||
// completed.
|
|
||||||
onCompleteUserStory: (BuildContext context) {
|
|
||||||
context.go(homePage);
|
|
||||||
},
|
|
||||||
|
|
||||||
// (RECOMMENDED) Handle processing of the order details. This function
|
|
||||||
// should return true if the order was processed successfully, otherwise
|
|
||||||
// false.
|
|
||||||
//
|
|
||||||
// If this function is not provided, it is assumed that the order is
|
|
||||||
// always processed successfully.
|
|
||||||
//
|
|
||||||
// Example use cases that could be implemented here:
|
|
||||||
// - Sending and storing the order on a server,
|
|
||||||
// - Processing payment (if the user decides to pay upfront).
|
|
||||||
// - And many more...
|
|
||||||
// onCompleteOrderDetails:
|
|
||||||
// (BuildContext context, OrderResult orderDetails) async {
|
|
||||||
// return true;
|
|
||||||
// },
|
|
||||||
);
|
|
|
@ -1,8 +0,0 @@
|
||||||
import 'package:flutter_shopping/flutter_shopping.dart';
|
|
||||||
|
|
||||||
class MyCategory extends ProductPageShop {
|
|
||||||
const MyCategory({
|
|
||||||
required super.id,
|
|
||||||
required super.name,
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
import "package:amazon/src/configuration/shopping_configuration.dart";
|
|
||||||
import "package:amazon/src/ui/homepage.dart";
|
|
||||||
import "package:amazon/src/utils/go_router.dart";
|
|
||||||
import "package:flutter_shopping/flutter_shopping.dart";
|
|
||||||
import "package:go_router/go_router.dart";
|
|
||||||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
|
||||||
|
|
||||||
const String homePage = "/";
|
|
||||||
|
|
||||||
final routerProvider = Provider<GoRouter>(
|
|
||||||
(ref) => GoRouter(
|
|
||||||
initialLocation: homePage,
|
|
||||||
routes: [
|
|
||||||
// Flutter Shopping Story Routes
|
|
||||||
...getShoppingStoryRoutes(
|
|
||||||
configuration: getFlutterShoppingConfiguration(),
|
|
||||||
),
|
|
||||||
// Home Route
|
|
||||||
GoRoute(
|
|
||||||
name: "home",
|
|
||||||
path: homePage,
|
|
||||||
pageBuilder: (context, state) => buildScreenWithFadeTransition(
|
|
||||||
context: context,
|
|
||||||
state: state,
|
|
||||||
child: const Homepage(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
|
@ -1,93 +0,0 @@
|
||||||
import "package:amazon/src/models/my_category.dart";
|
|
||||||
import "package:flutter_shopping/flutter_shopping.dart";
|
|
||||||
|
|
||||||
Map<String, String> categories = {
|
|
||||||
"Electronics": "Electronica",
|
|
||||||
"Smart phones": "Telefoons",
|
|
||||||
"TV's": "TV's",
|
|
||||||
};
|
|
||||||
|
|
||||||
List<Product> allProducts() => [
|
|
||||||
Product(
|
|
||||||
id: "1",
|
|
||||||
name:
|
|
||||||
"Skar Audio Single 8\" Complete 1,200 Watt EVL Series Subwoofer Bass Package - Includes Loaded Enclosure with...",
|
|
||||||
price: 2.99,
|
|
||||||
category: categories["Electronics"]!,
|
|
||||||
imageUrl:
|
|
||||||
"https://m.media-amazon.com/images/I/710n3hnbfXL._AC_UY218_.jpg",
|
|
||||||
description: "",
|
|
||||||
),
|
|
||||||
Product(
|
|
||||||
id: "2",
|
|
||||||
name:
|
|
||||||
"Frameo 10.1 Inch WiFi Digital Picture Frame, 1280x800 HD IPS Touch Screen Photo Frame Electronic, 32GB Memory, Auto...",
|
|
||||||
price: 2.99,
|
|
||||||
category: categories["Electronics"]!,
|
|
||||||
imageUrl:
|
|
||||||
"https://m.media-amazon.com/images/I/61O+aorCp0L._AC_UY218_.jpg",
|
|
||||||
description: "",
|
|
||||||
),
|
|
||||||
Product(
|
|
||||||
id: "3",
|
|
||||||
name:
|
|
||||||
"STREBITO Electronics Precision Screwdriver Sets 142-Piece with 120 Bits Magnetic Repair Tool Kit for iPhone, MacBook,...",
|
|
||||||
price: 1.99,
|
|
||||||
category: categories["Electronics"]!,
|
|
||||||
imageUrl:
|
|
||||||
"https://m.media-amazon.com/images/I/81-C7lGtQsL._AC_UY218_.jpg",
|
|
||||||
description: "",
|
|
||||||
),
|
|
||||||
Product(
|
|
||||||
id: "4",
|
|
||||||
name:
|
|
||||||
"Samsung Galaxy A15 (SM-155M/DSN), 128GB 6GB RAM, Dual SIM, Factory Unlocked GSM, International Version (Wall...",
|
|
||||||
price: 1.99,
|
|
||||||
category: categories["Smart phones"]!,
|
|
||||||
imageUrl:
|
|
||||||
"https://m.media-amazon.com/images/I/51rp0nqaPoL._AC_UY218_.jpg",
|
|
||||||
description: "",
|
|
||||||
),
|
|
||||||
Product(
|
|
||||||
id: "5",
|
|
||||||
name:
|
|
||||||
"SAMSUNG Galaxy S24 Ultra Cell Phone, 512GB AI Smartphone, Unlocked Android, 50MP Zoom Camera, Long...",
|
|
||||||
price: 1.99,
|
|
||||||
category: categories["Smart phones"]!,
|
|
||||||
imageUrl:
|
|
||||||
"https://m.media-amazon.com/images/I/71ZoDT7a2wL._AC_UY218_.jpg",
|
|
||||||
description: "",
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
List<MyCategory> getCategories() => <MyCategory>[
|
|
||||||
MyCategory(id: "1", name: categories["Electronics"]!),
|
|
||||||
MyCategory(id: "2", name: categories["Smart phones"]!),
|
|
||||||
MyCategory(id: "3", name: categories["TV's"]!),
|
|
||||||
const MyCategory(id: "4", name: "Monitoren"),
|
|
||||||
const MyCategory(id: "5", name: "Speakers"),
|
|
||||||
const MyCategory(id: "6", name: "Toetsenborden"),
|
|
||||||
];
|
|
||||||
|
|
||||||
ProductPageContent getShopContent(String shopId) {
|
|
||||||
var products = getProducts(shopId);
|
|
||||||
return ProductPageContent(
|
|
||||||
products: products,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Product> getProducts(String categoryId) {
|
|
||||||
if (categoryId == "1") {
|
|
||||||
return allProducts();
|
|
||||||
} else if (categoryId == "2") {
|
|
||||||
return allProducts()
|
|
||||||
.where((product) => product.category == categories["Smart phones"]!)
|
|
||||||
.toList();
|
|
||||||
} else if (categoryId == "3") {
|
|
||||||
return allProducts()
|
|
||||||
.where((product) => product.category == categories["TV's"]!)
|
|
||||||
.toList();
|
|
||||||
} else {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
import "package:flutter/material.dart";
|
|
||||||
import "package:flutter_shopping/flutter_shopping.dart";
|
|
||||||
import "package:go_router/go_router.dart";
|
|
||||||
|
|
||||||
class Homepage extends StatelessWidget {
|
|
||||||
const Homepage({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) => Scaffold(
|
|
||||||
body: Center(
|
|
||||||
child: Badge(
|
|
||||||
label: const Text("1"),
|
|
||||||
child: IconButton(
|
|
||||||
icon: const Icon(Icons.shopping_cart_outlined, size: 50),
|
|
||||||
onPressed: () => context.go(FlutterShoppingPathRoutes.shop),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
import "package:flutter/material.dart";
|
|
||||||
import "package:go_router/go_router.dart";
|
|
||||||
|
|
||||||
CustomTransitionPage buildScreenWithFadeTransition<T>({
|
|
||||||
required BuildContext context,
|
|
||||||
required GoRouterState state,
|
|
||||||
required Widget child,
|
|
||||||
}) =>
|
|
||||||
CustomTransitionPage<T>(
|
|
||||||
key: state.pageKey,
|
|
||||||
child: child,
|
|
||||||
transitionsBuilder: (context, animation, secondaryAnimation, child) =>
|
|
||||||
FadeTransition(opacity: animation, child: child),
|
|
||||||
);
|
|
||||||
|
|
||||||
CustomTransitionPage buildScreenWithoutTransition<T>({
|
|
||||||
required BuildContext context,
|
|
||||||
required GoRouterState state,
|
|
||||||
required Widget child,
|
|
||||||
}) =>
|
|
||||||
CustomTransitionPage<T>(
|
|
||||||
key: state.pageKey,
|
|
||||||
child: child,
|
|
||||||
transitionsBuilder: (context, animation, secondaryAnimation, child) =>
|
|
||||||
child,
|
|
||||||
);
|
|
|
@ -1,43 +0,0 @@
|
||||||
import "package:flutter/material.dart";
|
|
||||||
|
|
||||||
ThemeData getTheme() => ThemeData(
|
|
||||||
scaffoldBackgroundColor: const Color.fromRGBO(250, 249, 246, 1),
|
|
||||||
textTheme: const TextTheme(
|
|
||||||
labelMedium: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
titleMedium: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
color: Color.fromRGBO(60, 60, 59, 1),
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
inputDecorationTheme: const InputDecorationTheme(
|
|
||||||
fillColor: Colors.white,
|
|
||||||
),
|
|
||||||
colorScheme: const ColorScheme.light(
|
|
||||||
primary: Color.fromRGBO(161, 203, 211, 1),
|
|
||||||
secondary: Color.fromRGBO(221, 235, 238, 1),
|
|
||||||
surface: Color.fromRGBO(255, 255, 255, 1),
|
|
||||||
),
|
|
||||||
appBarTheme: const AppBarTheme(
|
|
||||||
backgroundColor: Color.fromRGBO(161, 220, 218, 1),
|
|
||||||
foregroundColor: Colors.black,
|
|
||||||
titleTextStyle: TextStyle(
|
|
||||||
fontSize: 28,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
filledButtonTheme: FilledButtonThemeData(
|
|
||||||
style: ButtonStyle(
|
|
||||||
backgroundColor: WidgetStateProperty.all(
|
|
||||||
Colors.yellow,
|
|
||||||
),
|
|
||||||
foregroundColor: WidgetStateProperty.all(
|
|
||||||
Colors.black,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
|
@ -1,31 +0,0 @@
|
||||||
name: amazon
|
|
||||||
description: "A new Flutter project."
|
|
||||||
publish_to: 'none'
|
|
||||||
version: 1.0.0
|
|
||||||
|
|
||||||
environment:
|
|
||||||
sdk: '>=3.4.1 <4.0.0'
|
|
||||||
|
|
||||||
dependencies:
|
|
||||||
flutter:
|
|
||||||
sdk: flutter
|
|
||||||
flutter_hooks: ^0.20.0
|
|
||||||
hooks_riverpod: ^2.1.1
|
|
||||||
go_router: 12.1.3
|
|
||||||
flutter_nested_categories:
|
|
||||||
git:
|
|
||||||
url: https://github.com/Iconica-Development/flutter_nested_categories
|
|
||||||
ref: 0.0.1
|
|
||||||
flutter_shopping:
|
|
||||||
git:
|
|
||||||
url: https://github.com/Iconica-Development/flutter_shopping
|
|
||||||
path: packages/flutter_shopping
|
|
||||||
ref: 2.0.0
|
|
||||||
|
|
||||||
dev_dependencies:
|
|
||||||
flutter_test:
|
|
||||||
sdk: flutter
|
|
||||||
flutter_lints: ^3.0.0
|
|
||||||
|
|
||||||
flutter:
|
|
||||||
uses-material-design: true
|
|
|
@ -1,11 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2022 Iconica
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
|
|
||||||
import "package:flutter_test/flutter_test.dart";
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
test("", () {
|
|
||||||
expect(true, true);
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -5,8 +5,5 @@ export "package:flutter_order_details/flutter_order_details.dart";
|
||||||
export "package:flutter_product_page/flutter_product_page.dart";
|
export "package:flutter_product_page/flutter_product_page.dart";
|
||||||
export "package:flutter_shopping_cart/flutter_shopping_cart.dart";
|
export "package:flutter_shopping_cart/flutter_shopping_cart.dart";
|
||||||
|
|
||||||
export "src/config/flutter_shopping_configuration.dart";
|
export "src/configuration/shopping_configuration.dart";
|
||||||
export "src/models/product.dart";
|
export "src/flutter_shopping_navigator_userstory.dart";
|
||||||
export "src/routes.dart";
|
|
||||||
export "src/user_stores/flutter_shopping_userstory_go_router.dart";
|
|
||||||
export "src/user_stores/flutter_shopping_userstory_navigation.dart";
|
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
import "package:flutter/material.dart";
|
|
||||||
import "package:flutter_order_details/flutter_order_details.dart";
|
|
||||||
|
|
||||||
/// Configuration class for the flutter_shopping user-story.
|
|
||||||
class FlutterShoppingConfiguration {
|
|
||||||
/// Constructor for the FlutterShoppingConfiguration.
|
|
||||||
const FlutterShoppingConfiguration({
|
|
||||||
required this.shopBuilder,
|
|
||||||
required this.shoppingCartBuilder,
|
|
||||||
required this.onCompleteUserStory,
|
|
||||||
this.orderDetailsBuilder,
|
|
||||||
this.onCompleteOrderDetails,
|
|
||||||
this.orderSuccessBuilder,
|
|
||||||
this.orderFailedBuilder,
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Builder for the shop/product page.
|
|
||||||
final Widget Function(
|
|
||||||
BuildContext context,
|
|
||||||
String? initialBuildShopId,
|
|
||||||
String? streetName,
|
|
||||||
) shopBuilder;
|
|
||||||
|
|
||||||
/// Builder for the shopping cart page.
|
|
||||||
final Widget Function(BuildContext context) shoppingCartBuilder;
|
|
||||||
|
|
||||||
/// Function that is called when the user-story is completed.
|
|
||||||
final Function(BuildContext context) onCompleteUserStory;
|
|
||||||
|
|
||||||
/// Builder for the order details page. This does not have to be set if you
|
|
||||||
/// are using the default order details page.
|
|
||||||
final Widget Function(BuildContext context)? orderDetailsBuilder;
|
|
||||||
|
|
||||||
/// Allows you to execute actions before
|
|
||||||
final Future<bool> Function(BuildContext context, OrderResult result)?
|
|
||||||
onCompleteOrderDetails;
|
|
||||||
|
|
||||||
/// Builder for when the order is successful.
|
|
||||||
final Widget Function(BuildContext context)? orderSuccessBuilder;
|
|
||||||
|
|
||||||
/// Builder for when the order failed.
|
|
||||||
final Widget Function(BuildContext context)? orderFailedBuilder;
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
/// Contains the localized strings for the order details screen.
|
|
||||||
class OrderDetailsLocalizations {
|
|
||||||
/// Creates order details localizations
|
|
||||||
OrderDetailsLocalizations({
|
|
||||||
this.orderDetailsTitle = "Information",
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Title for the order details screen.
|
|
||||||
final String orderDetailsTitle;
|
|
||||||
}
|
|
|
@ -0,0 +1,218 @@
|
||||||
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_shopping/flutter_shopping.dart";
|
||||||
|
import "package:flutter_shopping_interface/flutter_shopping_interface.dart";
|
||||||
|
|
||||||
|
/// configuration for the shopping userstory
|
||||||
|
class ShoppingConfiguration {
|
||||||
|
/// constructor for the userstory configuration
|
||||||
|
const ShoppingConfiguration({
|
||||||
|
/// ProductPage configurations
|
||||||
|
required this.shoppingService,
|
||||||
|
this.onGetProducts,
|
||||||
|
this.onGetShops,
|
||||||
|
this.onAddToCart,
|
||||||
|
this.onNavigateToShoppingCart,
|
||||||
|
this.getProductsInShoppingCart,
|
||||||
|
this.shoppingCartButtonBuilder,
|
||||||
|
this.initialShopid,
|
||||||
|
this.productBuilder,
|
||||||
|
this.onShopSelectionChange,
|
||||||
|
this.productPageTranslations,
|
||||||
|
this.shopSelectorStyle,
|
||||||
|
this.productPagePagePadding,
|
||||||
|
this.productPageAppBarBuilder,
|
||||||
|
this.bottomNavigationBarBuilder,
|
||||||
|
this.onProductDetail,
|
||||||
|
this.discountDescription,
|
||||||
|
this.noContentBuilder,
|
||||||
|
this.errorBuilder,
|
||||||
|
|
||||||
|
/// ShoppingCart configurations
|
||||||
|
this.onConfirmOrder,
|
||||||
|
this.productItemBuilder,
|
||||||
|
this.confirmOrderButtonBuilder,
|
||||||
|
this.confirmOrderButtonHeight,
|
||||||
|
this.sumBottomSheetBuilder,
|
||||||
|
this.sumBottomSheetHeight,
|
||||||
|
this.titleBuilder,
|
||||||
|
this.shoppingCartTranslations,
|
||||||
|
this.shoppingCartPagePadding,
|
||||||
|
this.shoppingCartBottomPadding,
|
||||||
|
this.shoppingCartAppBarBuilder,
|
||||||
|
|
||||||
|
/// OrderDetail configurations
|
||||||
|
this.onNextStep,
|
||||||
|
this.onStepsCompleted,
|
||||||
|
this.onCompleteOrderDetails,
|
||||||
|
this.pages,
|
||||||
|
this.orderDetailTranslations,
|
||||||
|
this.orderDetailAppBarBuilder,
|
||||||
|
this.orderDetailNextbuttonBuilder,
|
||||||
|
this.orderSuccessBuilder,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// The service that will be used for the userstory
|
||||||
|
final ShoppingService shoppingService;
|
||||||
|
|
||||||
|
/// Function that will be called when the products are requested
|
||||||
|
final Future<List<Product>> Function(String shopId)? onGetProducts;
|
||||||
|
|
||||||
|
/// Function that will be called when the shops are requested
|
||||||
|
final Future<List<Shop>> Function()? onGetShops;
|
||||||
|
|
||||||
|
/// Function that will be called when an item is added to the shopping cart
|
||||||
|
final Function(Product)? onAddToCart;
|
||||||
|
|
||||||
|
/// Function that will be called when the user navigates to the shopping cart
|
||||||
|
final Function()? onNavigateToShoppingCart;
|
||||||
|
|
||||||
|
/// Function that will be called to get the amount of
|
||||||
|
/// products in the shopping cart
|
||||||
|
final int Function()? getProductsInShoppingCart;
|
||||||
|
|
||||||
|
/// Default shopping cart button builder
|
||||||
|
final Widget Function(BuildContext, ProductPageConfiguration)?
|
||||||
|
shoppingCartButtonBuilder;
|
||||||
|
|
||||||
|
/// Initial shop that will be selected
|
||||||
|
final String? initialShopid;
|
||||||
|
|
||||||
|
/// ProductPage item builder
|
||||||
|
final Widget Function(
|
||||||
|
BuildContext,
|
||||||
|
Product,
|
||||||
|
ProductPageConfiguration configuration,
|
||||||
|
)? productBuilder;
|
||||||
|
|
||||||
|
/// Function that will be called when the shop selection changes
|
||||||
|
final Function(Shop)? onShopSelectionChange;
|
||||||
|
|
||||||
|
/// Translations for the product page
|
||||||
|
final ProductPageTranslations? productPageTranslations;
|
||||||
|
|
||||||
|
/// Shop selector style
|
||||||
|
final ShopSelectorStyle? shopSelectorStyle;
|
||||||
|
|
||||||
|
/// ProductPage padding
|
||||||
|
final EdgeInsets? productPagePagePadding;
|
||||||
|
|
||||||
|
/// AppBar builder
|
||||||
|
final AppBar Function(BuildContext)? productPageAppBarBuilder;
|
||||||
|
|
||||||
|
/// BottomNavigationBarBuilder
|
||||||
|
final Widget? bottomNavigationBarBuilder;
|
||||||
|
|
||||||
|
/// Function that will be called when the product detail is requested
|
||||||
|
final Function(BuildContext, Product, String)? onProductDetail;
|
||||||
|
|
||||||
|
/// Function that will be called when the discount description is requested
|
||||||
|
final String Function(Product)? discountDescription;
|
||||||
|
|
||||||
|
/// Function that will be called when there are no products
|
||||||
|
final Widget Function(BuildContext)? noContentBuilder;
|
||||||
|
|
||||||
|
/// Function that will be called when there is an error
|
||||||
|
final Widget Function(BuildContext, Object?, StackTrace?)? errorBuilder;
|
||||||
|
|
||||||
|
/// Function that will be called when the order button on
|
||||||
|
/// the shopping cart page is pressed
|
||||||
|
final Function(List<Product>)? onConfirmOrder;
|
||||||
|
|
||||||
|
/// Shopping cart item builder
|
||||||
|
final Widget Function(BuildContext, Product, ShoppingCartConfig)?
|
||||||
|
productItemBuilder;
|
||||||
|
|
||||||
|
/// Shopping cart confirm order button builder
|
||||||
|
final Widget Function(
|
||||||
|
BuildContext,
|
||||||
|
ShoppingCartConfig,
|
||||||
|
dynamic Function(List<Product>),
|
||||||
|
)? confirmOrderButtonBuilder;
|
||||||
|
|
||||||
|
/// The height of the confirm order button
|
||||||
|
/// This will not set the height of the button itself
|
||||||
|
/// this is only used to create some extra space on the bottom
|
||||||
|
/// of the product list so the button doesn't overlap with the
|
||||||
|
/// last product
|
||||||
|
final double? confirmOrderButtonHeight;
|
||||||
|
|
||||||
|
/// Shopping cart sum bottom sheet builder
|
||||||
|
final Widget Function(BuildContext, ShoppingCartConfig)?
|
||||||
|
sumBottomSheetBuilder;
|
||||||
|
|
||||||
|
/// The height of the sum bottom sheet
|
||||||
|
/// This will not set the height of the sheet itself
|
||||||
|
/// this is only used to create some extra space on the bottom
|
||||||
|
/// of the product list so the sheet doesn't overlap with the
|
||||||
|
/// last product
|
||||||
|
final double? sumBottomSheetHeight;
|
||||||
|
|
||||||
|
/// Function to override the title on the shopping cart screen
|
||||||
|
final Widget Function(BuildContext, String)? titleBuilder;
|
||||||
|
|
||||||
|
/// Shopping cart translations
|
||||||
|
final ShoppingCartTranslations? shoppingCartTranslations;
|
||||||
|
|
||||||
|
/// Shopping cart page padding
|
||||||
|
final EdgeInsets? shoppingCartPagePadding;
|
||||||
|
|
||||||
|
/// Shopping cart bottom padding
|
||||||
|
final EdgeInsets? shoppingCartBottomPadding;
|
||||||
|
|
||||||
|
/// Shopping cart app bar builder
|
||||||
|
final AppBar Function(BuildContext)? shoppingCartAppBarBuilder;
|
||||||
|
|
||||||
|
// = _defaultPages,
|
||||||
|
// = const OrderDetailTranslations(),
|
||||||
|
// = _defaultAppBar,
|
||||||
|
// = _defaultNextButtonBuilder,
|
||||||
|
// = _defaultOrderSuccess,
|
||||||
|
|
||||||
|
/// Function that gets called when the user navigates to the next
|
||||||
|
/// step of the order details
|
||||||
|
final dynamic Function(
|
||||||
|
int,
|
||||||
|
Map<String, dynamic>,
|
||||||
|
FlutterFormController controller,
|
||||||
|
)? onNextStep;
|
||||||
|
|
||||||
|
/// Function that gets called when the Navigates
|
||||||
|
/// to the order confirmationp page
|
||||||
|
final dynamic Function(
|
||||||
|
String,
|
||||||
|
List<Product>,
|
||||||
|
Map<int, Map<String, dynamic>>,
|
||||||
|
OrderDetailConfiguration,
|
||||||
|
)? onStepsCompleted;
|
||||||
|
|
||||||
|
/// Function that gets called when pressing the complete order
|
||||||
|
/// button on the confirmation page
|
||||||
|
final Function(BuildContext, OrderDetailConfiguration)?
|
||||||
|
onCompleteOrderDetails;
|
||||||
|
|
||||||
|
/// The order detail pages that are used in the order detail screen
|
||||||
|
final List<FlutterFormPage> Function(BuildContext)? pages;
|
||||||
|
|
||||||
|
/// The translations for the order detail screen
|
||||||
|
final OrderDetailTranslations? orderDetailTranslations;
|
||||||
|
|
||||||
|
/// The app bar for the order detail screen
|
||||||
|
final AppBar Function(BuildContext, String)? orderDetailAppBarBuilder;
|
||||||
|
|
||||||
|
/// The builder for the next button on the order detail screen
|
||||||
|
final Widget Function(
|
||||||
|
int,
|
||||||
|
// ignore: avoid_positional_boolean_parameters
|
||||||
|
bool,
|
||||||
|
BuildContext,
|
||||||
|
OrderDetailConfiguration,
|
||||||
|
FlutterFormController,
|
||||||
|
)? orderDetailNextbuttonBuilder;
|
||||||
|
|
||||||
|
/// The builder for the order success screen
|
||||||
|
final Widget Function(
|
||||||
|
BuildContext,
|
||||||
|
OrderDetailConfiguration,
|
||||||
|
Map<int, Map<String, dynamic>>,
|
||||||
|
)? orderSuccessBuilder;
|
||||||
|
}
|
|
@ -0,0 +1,209 @@
|
||||||
|
// ignore_for_file: public_member_api_docs
|
||||||
|
|
||||||
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_shopping/flutter_shopping.dart";
|
||||||
|
import "package:flutter_shopping_local/flutter_shopping_local.dart";
|
||||||
|
|
||||||
|
class ShoppingNavigatorUserStory extends StatelessWidget {
|
||||||
|
const ShoppingNavigatorUserStory({
|
||||||
|
this.shoppingConfiguration,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final ShoppingConfiguration? shoppingConfiguration;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => ShoppingProductPage(
|
||||||
|
shoppingConfiguration: shoppingConfiguration ??
|
||||||
|
ShoppingConfiguration(
|
||||||
|
shoppingService: LocalShoppingService(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ShoppingProductPage extends StatelessWidget {
|
||||||
|
const ShoppingProductPage({
|
||||||
|
required this.shoppingConfiguration,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
final ShoppingConfiguration shoppingConfiguration;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var service = shoppingConfiguration.shoppingService;
|
||||||
|
return ProductPageScreen(
|
||||||
|
initialBuildShopId: shoppingConfiguration.initialShopid,
|
||||||
|
configuration: ProductPageConfiguration(
|
||||||
|
shoppingService: service,
|
||||||
|
initialShopId: shoppingConfiguration.initialShopid,
|
||||||
|
shoppingCartButtonBuilder:
|
||||||
|
shoppingConfiguration.shoppingCartButtonBuilder,
|
||||||
|
productBuilder: shoppingConfiguration.productBuilder,
|
||||||
|
onShopSelectionChange: shoppingConfiguration.onShopSelectionChange,
|
||||||
|
translations: shoppingConfiguration.productPageTranslations,
|
||||||
|
shopSelectorStyle: shoppingConfiguration.shopSelectorStyle,
|
||||||
|
pagePadding: shoppingConfiguration.productPagePagePadding,
|
||||||
|
appBar: shoppingConfiguration.productPageAppBarBuilder,
|
||||||
|
bottomNavigationBar: shoppingConfiguration.bottomNavigationBarBuilder,
|
||||||
|
onProductDetail: shoppingConfiguration.onProductDetail,
|
||||||
|
discountDescription: shoppingConfiguration.discountDescription,
|
||||||
|
noContentBuilder: shoppingConfiguration.noContentBuilder,
|
||||||
|
errorBuilder: shoppingConfiguration.errorBuilder,
|
||||||
|
shops: () async {
|
||||||
|
if (shoppingConfiguration.onGetShops != null) {
|
||||||
|
return shoppingConfiguration.onGetShops!();
|
||||||
|
} else {
|
||||||
|
return service.shopService.getShops();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getProducts: (shop) async {
|
||||||
|
if (shoppingConfiguration.onGetProducts != null) {
|
||||||
|
return shoppingConfiguration.onGetProducts!(shop.id);
|
||||||
|
} else {
|
||||||
|
return service.productService.getProducts(shop.id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onAddToCart: (product) {
|
||||||
|
if (shoppingConfiguration.onAddToCart != null) {
|
||||||
|
shoppingConfiguration.onAddToCart!(product);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
return service.shoppingCartService.addProduct(product);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onNavigateToShoppingCart: () async {
|
||||||
|
if (shoppingConfiguration.onNavigateToShoppingCart != null) {
|
||||||
|
return shoppingConfiguration.onNavigateToShoppingCart!();
|
||||||
|
} else {
|
||||||
|
return Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ShoppingCart(
|
||||||
|
shoppingConfiguration: shoppingConfiguration,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getProductsInShoppingCart: () {
|
||||||
|
if (shoppingConfiguration.getProductsInShoppingCart != null) {
|
||||||
|
return shoppingConfiguration.getProductsInShoppingCart!();
|
||||||
|
} else {
|
||||||
|
return service.shoppingCartService.countProducts();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ShoppingCart extends StatelessWidget {
|
||||||
|
const ShoppingCart({
|
||||||
|
required this.shoppingConfiguration,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final ShoppingConfiguration shoppingConfiguration;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var service = shoppingConfiguration.shoppingService.shoppingCartService;
|
||||||
|
return ShoppingCartScreen(
|
||||||
|
configuration: ShoppingCartConfig(
|
||||||
|
service: service,
|
||||||
|
productItemBuilder: shoppingConfiguration.productItemBuilder,
|
||||||
|
confirmOrderButtonBuilder:
|
||||||
|
shoppingConfiguration.confirmOrderButtonBuilder,
|
||||||
|
confirmOrderButtonHeight:
|
||||||
|
shoppingConfiguration.confirmOrderButtonHeight,
|
||||||
|
sumBottomSheetBuilder: shoppingConfiguration.sumBottomSheetBuilder,
|
||||||
|
sumBottomSheetHeight: shoppingConfiguration.sumBottomSheetHeight,
|
||||||
|
titleBuilder: shoppingConfiguration.titleBuilder,
|
||||||
|
translations: shoppingConfiguration.shoppingCartTranslations,
|
||||||
|
pagePadding: shoppingConfiguration.shoppingCartPagePadding,
|
||||||
|
bottomPadding: shoppingConfiguration.shoppingCartBottomPadding,
|
||||||
|
appBar: shoppingConfiguration.shoppingCartAppBarBuilder,
|
||||||
|
onConfirmOrder: (products) async {
|
||||||
|
if (shoppingConfiguration.onConfirmOrder != null) {
|
||||||
|
return shoppingConfiguration.onConfirmOrder!(products);
|
||||||
|
} else {
|
||||||
|
return Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ShoppingOrderDetails(
|
||||||
|
shoppingConfiguration: shoppingConfiguration,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ShoppingOrderDetails extends StatelessWidget {
|
||||||
|
const ShoppingOrderDetails({
|
||||||
|
required this.shoppingConfiguration,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final ShoppingConfiguration shoppingConfiguration;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => OrderDetailScreen(
|
||||||
|
configuration: OrderDetailConfiguration(
|
||||||
|
shoppingService: shoppingConfiguration.shoppingService,
|
||||||
|
pages: shoppingConfiguration.pages,
|
||||||
|
translations: shoppingConfiguration.orderDetailTranslations,
|
||||||
|
appBar: shoppingConfiguration.orderDetailAppBarBuilder,
|
||||||
|
nextbuttonBuilder: shoppingConfiguration.orderDetailNextbuttonBuilder,
|
||||||
|
orderSuccessBuilder: shoppingConfiguration.orderSuccessBuilder,
|
||||||
|
onNextStep: (currentStep, data, controller) async {
|
||||||
|
if (shoppingConfiguration.onNextStep != null) {
|
||||||
|
return shoppingConfiguration.onNextStep!(
|
||||||
|
currentStep,
|
||||||
|
data,
|
||||||
|
controller,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await controller.autoNextStep();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onStepsCompleted: (shopId, products, data, configuration) async {
|
||||||
|
if (shoppingConfiguration.onStepsCompleted != null) {
|
||||||
|
return shoppingConfiguration.onStepsCompleted!(
|
||||||
|
shopId,
|
||||||
|
products,
|
||||||
|
data,
|
||||||
|
configuration,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Navigator.of(context).pushReplacement(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => DefaultOrderSucces(
|
||||||
|
configuration: configuration,
|
||||||
|
orderDetails: data,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onCompleteOrderDetails: (context, configuration) async {
|
||||||
|
if (shoppingConfiguration.onCompleteOrderDetails != null) {
|
||||||
|
return shoppingConfiguration.onCompleteOrderDetails!(
|
||||||
|
context,
|
||||||
|
configuration,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
shoppingConfiguration.shoppingService.shoppingCartService.clear();
|
||||||
|
return Navigator.of(context).pushReplacement(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ShoppingProductPage(
|
||||||
|
shoppingConfiguration: shoppingConfiguration,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,28 +0,0 @@
|
||||||
import "package:flutter/material.dart";
|
|
||||||
import "package:go_router/go_router.dart";
|
|
||||||
|
|
||||||
/// Builder with a fade transition for when navigating to a new screen.
|
|
||||||
CustomTransitionPage buildScreenWithFadeTransition<T>({
|
|
||||||
required BuildContext context,
|
|
||||||
required GoRouterState state,
|
|
||||||
required Widget child,
|
|
||||||
}) =>
|
|
||||||
CustomTransitionPage<T>(
|
|
||||||
key: state.pageKey,
|
|
||||||
child: child,
|
|
||||||
transitionsBuilder: (context, animation, secondaryAnimation, child) =>
|
|
||||||
FadeTransition(opacity: animation, child: child),
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Builder without a transition for when navigating to a new screen.
|
|
||||||
CustomTransitionPage buildScreenWithoutTransition<T>({
|
|
||||||
required BuildContext context,
|
|
||||||
required GoRouterState state,
|
|
||||||
required Widget child,
|
|
||||||
}) =>
|
|
||||||
CustomTransitionPage<T>(
|
|
||||||
key: state.pageKey,
|
|
||||||
child: child,
|
|
||||||
transitionsBuilder: (context, animation, secondaryAnimation, child) =>
|
|
||||||
child,
|
|
||||||
);
|
|
|
@ -1,43 +0,0 @@
|
||||||
/// The product class contains all the information that a product can have.
|
|
||||||
/// This class is used in the shopping cart and the product page.
|
|
||||||
class Product {
|
|
||||||
/// Creates a product.
|
|
||||||
Product({
|
|
||||||
required this.id,
|
|
||||||
required this.name,
|
|
||||||
required this.imageUrl,
|
|
||||||
required this.category,
|
|
||||||
required this.price,
|
|
||||||
required this.description,
|
|
||||||
this.hasDiscount = false,
|
|
||||||
this.discountPrice,
|
|
||||||
this.quantity = 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
/// The unique identifier for the product.
|
|
||||||
final String id;
|
|
||||||
|
|
||||||
/// The name of the product.
|
|
||||||
final String name;
|
|
||||||
|
|
||||||
/// The image URL of the product.
|
|
||||||
final String imageUrl;
|
|
||||||
|
|
||||||
/// The category of the product.
|
|
||||||
final String category;
|
|
||||||
|
|
||||||
/// The price of the product.
|
|
||||||
final double price;
|
|
||||||
|
|
||||||
/// Whether the product has a discount or not.
|
|
||||||
final bool hasDiscount;
|
|
||||||
|
|
||||||
/// The discounted price of the product. Only used if [hasDiscount] is true.
|
|
||||||
final double? discountPrice;
|
|
||||||
|
|
||||||
/// Quantity for the product.
|
|
||||||
int quantity;
|
|
||||||
|
|
||||||
/// The description of the product.
|
|
||||||
final String description;
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
/// All the name routes used in the user-story.
|
|
||||||
mixin FlutterShoppingNameRoutes {
|
|
||||||
/// The shop name route.
|
|
||||||
static const String shop = "shop";
|
|
||||||
|
|
||||||
/// The shopping cart name route.
|
|
||||||
static const String shoppingCart = "shoppingcart";
|
|
||||||
|
|
||||||
/// The order details name route.
|
|
||||||
static const String orderDetails = "orderdetails";
|
|
||||||
|
|
||||||
/// The order success name route.
|
|
||||||
static const String orderSuccess = "ordersuccess";
|
|
||||||
|
|
||||||
/// The order failed name route.
|
|
||||||
static const String orderFailed = "orderfailed";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// All the path routes used in the user-story.
|
|
||||||
mixin FlutterShoppingPathRoutes {
|
|
||||||
/// The shop page route.
|
|
||||||
static const String shop = "/shop";
|
|
||||||
|
|
||||||
/// The shopping cart page route.
|
|
||||||
static const String shoppingCart = "/shopping-cart";
|
|
||||||
|
|
||||||
/// The order details page route.
|
|
||||||
static const String orderDetails = "/order-details";
|
|
||||||
|
|
||||||
/// The order success page route.
|
|
||||||
static const String orderSuccess = "/order-success";
|
|
||||||
|
|
||||||
/// The order failed page route.
|
|
||||||
static const String orderFailed = "/order-failed";
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
import "package:flutter_shopping/flutter_shopping.dart";
|
|
||||||
import "package:flutter_shopping/src/widgets/default_order_failed_widget.dart";
|
|
||||||
import "package:flutter_shopping/src/widgets/default_order_succes_widget.dart";
|
|
||||||
import "package:go_router/go_router.dart";
|
|
||||||
|
|
||||||
/// All the routes for the shopping story.
|
|
||||||
List<GoRoute> getShoppingStoryRoutes({
|
|
||||||
required FlutterShoppingConfiguration configuration,
|
|
||||||
}) =>
|
|
||||||
<GoRoute>[
|
|
||||||
GoRoute(
|
|
||||||
name: FlutterShoppingNameRoutes.shop,
|
|
||||||
path: FlutterShoppingPathRoutes.shop,
|
|
||||||
builder: (context, state) => configuration.shopBuilder(
|
|
||||||
context,
|
|
||||||
state.uri.queryParameters["id"],
|
|
||||||
state.uri.queryParameters["street"],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: FlutterShoppingNameRoutes.shoppingCart,
|
|
||||||
path: FlutterShoppingPathRoutes.shoppingCart,
|
|
||||||
builder: (context, state) => configuration.shoppingCartBuilder(context),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: FlutterShoppingNameRoutes.orderDetails,
|
|
||||||
path: FlutterShoppingPathRoutes.orderDetails,
|
|
||||||
builder: (context, state) => configuration.orderDetailsBuilder != null
|
|
||||||
? configuration.orderDetailsBuilder!(context)
|
|
||||||
: OrderDetailScreen(
|
|
||||||
configuration: OrderDetailConfiguration(
|
|
||||||
onCompleted: (result) {
|
|
||||||
context.go(FlutterShoppingPathRoutes.orderSuccess);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: FlutterShoppingNameRoutes.orderSuccess,
|
|
||||||
path: FlutterShoppingPathRoutes.orderSuccess,
|
|
||||||
builder: (context, state) => configuration.orderSuccessBuilder != null
|
|
||||||
? configuration.orderSuccessBuilder!(context)
|
|
||||||
: DefaultOrderSucces(configuration: configuration),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: FlutterShoppingNameRoutes.orderFailed,
|
|
||||||
path: FlutterShoppingPathRoutes.orderFailed,
|
|
||||||
builder: (context, state) => configuration.orderFailedBuilder != null
|
|
||||||
? configuration.orderFailedBuilder!(context)
|
|
||||||
: DefaultOrderFailed(configuration: configuration),
|
|
||||||
),
|
|
||||||
];
|
|
|
@ -1,52 +0,0 @@
|
||||||
import "package:flutter/material.dart";
|
|
||||||
import "package:flutter_shopping/flutter_shopping.dart";
|
|
||||||
import "package:go_router/go_router.dart";
|
|
||||||
|
|
||||||
/// Default on complete order details function.
|
|
||||||
/// This function will navigate to the order success or order failed page.
|
|
||||||
///
|
|
||||||
/// You can create your own implementation if you decide to use a different
|
|
||||||
/// approach.
|
|
||||||
Future<void> onCompleteOrderDetails(
|
|
||||||
BuildContext context,
|
|
||||||
FlutterShoppingConfiguration configuration,
|
|
||||||
OrderResult result,
|
|
||||||
) async {
|
|
||||||
var go = context.go;
|
|
||||||
var succesful = true;
|
|
||||||
|
|
||||||
if (configuration.onCompleteOrderDetails != null) {
|
|
||||||
var executionResult =
|
|
||||||
await configuration.onCompleteOrderDetails?.call(context, result);
|
|
||||||
|
|
||||||
if (executionResult == null || !executionResult) {
|
|
||||||
succesful = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (succesful) {
|
|
||||||
go(FlutterShoppingPathRoutes.orderSuccess);
|
|
||||||
} else {
|
|
||||||
go(FlutterShoppingPathRoutes.orderFailed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Default on complete shopping cart function.
|
|
||||||
///
|
|
||||||
/// You can create your own implementation if you decide to use a different
|
|
||||||
/// approach.
|
|
||||||
Future<void> onCompleteShoppingCart(
|
|
||||||
BuildContext context,
|
|
||||||
) async {
|
|
||||||
await context.push(FlutterShoppingPathRoutes.orderDetails);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Default on complete product page function.
|
|
||||||
///
|
|
||||||
/// You can create your own implementation if you decide to use a different
|
|
||||||
/// approach.
|
|
||||||
Future<void> onCompleteProductPage(
|
|
||||||
BuildContext context,
|
|
||||||
) async {
|
|
||||||
await context.push(FlutterShoppingPathRoutes.shoppingCart);
|
|
||||||
}
|
|
|
@ -1,10 +1,10 @@
|
||||||
name: flutter_shopping
|
name: flutter_shopping
|
||||||
description: "A new Flutter project."
|
description: "A new Flutter project."
|
||||||
publish_to: 'none'
|
publish_to: "none"
|
||||||
version: 2.0.0
|
version: 2.0.0
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.3.4 <4.0.0'
|
sdk: ">=3.3.4 <4.0.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
|
@ -25,6 +25,16 @@ dependencies:
|
||||||
url: https://github.com/Iconica-Development/flutter_shopping
|
url: https://github.com/Iconica-Development/flutter_shopping
|
||||||
ref: 2.0.0
|
ref: 2.0.0
|
||||||
path: packages/flutter_order_details
|
path: packages/flutter_order_details
|
||||||
|
flutter_shopping_interface:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_shopping
|
||||||
|
ref: 2.0.0
|
||||||
|
path: packages/flutter_shopping_interface
|
||||||
|
flutter_shopping_local:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_shopping
|
||||||
|
ref: 2.0.0
|
||||||
|
path: packages/flutter_shopping_local
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
@ -35,4 +45,3 @@ dev_dependencies:
|
||||||
ref: 7.0.0
|
ref: 7.0.0
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
|
|
||||||
|
|
|
@ -11,17 +11,27 @@ class ShoppingCartConfig {
|
||||||
ShoppingCartConfig({
|
ShoppingCartConfig({
|
||||||
required this.service,
|
required this.service,
|
||||||
required this.onConfirmOrder,
|
required this.onConfirmOrder,
|
||||||
this.productItemBuilder = _defaultProductItemBuilder,
|
this.productItemBuilder,
|
||||||
this.confirmOrderButtonBuilder = _defaultConfirmOrderButton,
|
this.confirmOrderButtonBuilder,
|
||||||
this.confirmOrderButtonHeight = 100,
|
this.confirmOrderButtonHeight,
|
||||||
this.sumBottomSheetBuilder = _defaultSumBottomSheetBuilder,
|
this.sumBottomSheetBuilder,
|
||||||
this.sumBottomSheetHeight = 100,
|
this.sumBottomSheetHeight,
|
||||||
this.titleBuilder,
|
this.titleBuilder,
|
||||||
this.translations = const ShoppingCartTranslations(),
|
this.translations,
|
||||||
this.pagePadding = const EdgeInsets.symmetric(horizontal: 32),
|
this.pagePadding,
|
||||||
this.bottomPadding = const EdgeInsets.fromLTRB(44, 0, 44, 32),
|
this.bottomPadding,
|
||||||
this.appBar = _defaultAppBar,
|
this.appBar,
|
||||||
});
|
}) {
|
||||||
|
productItemBuilder ??= _defaultProductItemBuilder;
|
||||||
|
confirmOrderButtonBuilder ??= _defaultConfirmOrderButton;
|
||||||
|
sumBottomSheetBuilder ??= _defaultSumBottomSheetBuilder;
|
||||||
|
appBar ??= _defaultAppBar;
|
||||||
|
translations ??= const ShoppingCartTranslations();
|
||||||
|
pagePadding ??= const EdgeInsets.symmetric(horizontal: 32);
|
||||||
|
bottomPadding ??= const EdgeInsets.fromLTRB(44, 0, 44, 32);
|
||||||
|
confirmOrderButtonHeight ??= 100;
|
||||||
|
sumBottomSheetHeight ??= 100;
|
||||||
|
}
|
||||||
|
|
||||||
/// Product service. The product service is used to manage the products in the
|
/// Product service. The product service is used to manage the products in the
|
||||||
/// shopping cart.
|
/// shopping cart.
|
||||||
|
@ -29,26 +39,26 @@ class ShoppingCartConfig {
|
||||||
|
|
||||||
/// 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(
|
Widget Function(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
Product product,
|
Product product,
|
||||||
ShoppingCartConfig configuration,
|
ShoppingCartConfig configuration,
|
||||||
) productItemBuilder;
|
)? productItemBuilder;
|
||||||
|
|
||||||
/// 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(
|
Widget Function(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
ShoppingCartConfig configuration,
|
ShoppingCartConfig configuration,
|
||||||
Function(List<Product> products) onConfirmOrder,
|
Function(List<Product> products) onConfirmOrder,
|
||||||
) confirmOrderButtonBuilder;
|
)? 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.
|
||||||
/// If you override the confirm order button builder, you must provide a
|
/// If you override the confirm order button builder, you must provide a
|
||||||
/// height.
|
/// height.
|
||||||
final double confirmOrderButtonHeight;
|
double? confirmOrderButtonHeight;
|
||||||
|
|
||||||
/// 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
|
||||||
|
@ -58,22 +68,22 @@ class ShoppingCartConfig {
|
||||||
/// 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, ShoppingCartConfig configuration)
|
Widget Function(BuildContext context, ShoppingCartConfig configuration)?
|
||||||
sumBottomSheetBuilder;
|
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.
|
||||||
/// If you override the sum bottom sheet builder, you must provide a height.
|
/// If you override the sum bottom sheet builder, you must provide a height.
|
||||||
final double sumBottomSheetHeight;
|
double? sumBottomSheetHeight;
|
||||||
|
|
||||||
/// 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 pagePadding;
|
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;
|
EdgeInsets? bottomPadding;
|
||||||
|
|
||||||
/// Title builder. This builder is used to
|
/// Title builder. This builder is used to
|
||||||
/// build the title of the shopping cart.
|
/// build the title of the shopping cart.
|
||||||
|
@ -83,10 +93,10 @@ class ShoppingCartConfig {
|
||||||
)? titleBuilder;
|
)? titleBuilder;
|
||||||
|
|
||||||
/// Shopping cart translations. The translations for the shopping cart.
|
/// Shopping cart translations. The translations for the shopping cart.
|
||||||
final ShoppingCartTranslations translations;
|
ShoppingCartTranslations? translations;
|
||||||
|
|
||||||
/// Appbar for the shopping cart screen.
|
/// Appbar for the shopping cart screen.
|
||||||
final AppBar Function(BuildContext context) appBar;
|
AppBar Function(BuildContext context)? appBar;
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _defaultProductItemBuilder(
|
Widget _defaultProductItemBuilder(
|
||||||
|
@ -217,11 +227,11 @@ Widget _defaultSumBottomSheetBuilder(
|
||||||
);
|
);
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: configuration.bottomPadding,
|
padding: configuration.bottomPadding!,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
configuration.translations.sum,
|
configuration.translations!.sum,
|
||||||
style: theme.textTheme.titleMedium,
|
style: theme.textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
|
@ -261,7 +271,7 @@ Widget _defaultConfirmOrderButton(
|
||||||
vertical: 12,
|
vertical: 12,
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
configuration.translations.placeOrder,
|
configuration.translations!.placeOrder,
|
||||||
style: theme.textTheme.displayLarge,
|
style: theme.textTheme.displayLarge,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -50,7 +50,7 @@ class ProductItemPopup extends StatelessWidget {
|
||||||
vertical: 8.0,
|
vertical: 8.0,
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
configuration.translations.close,
|
configuration.translations!.close,
|
||||||
style: theme.textTheme.displayLarge,
|
style: theme.textTheme.displayLarge,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -17,20 +17,20 @@ class ShoppingCartScreen extends StatelessWidget {
|
||||||
var theme = Theme.of(context);
|
var theme = Theme.of(context);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: configuration.appBar.call(context),
|
appBar: configuration.appBar?.call(context),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Stack(
|
child: Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: configuration.pagePadding,
|
padding: configuration.pagePadding!,
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
if (configuration.titleBuilder != null) ...{
|
if (configuration.titleBuilder != null) ...{
|
||||||
configuration.titleBuilder!(
|
configuration.titleBuilder!(
|
||||||
context,
|
context,
|
||||||
configuration.translations.cartTitle,
|
configuration.translations!.cartTitle,
|
||||||
),
|
),
|
||||||
} else ...{
|
} else ...{
|
||||||
Padding(
|
Padding(
|
||||||
|
@ -40,7 +40,7 @@ class ShoppingCartScreen extends StatelessWidget {
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
configuration.translations.cartTitle,
|
configuration.translations!.cartTitle,
|
||||||
style: theme.textTheme.titleLarge,
|
style: theme.textTheme.titleLarge,
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
),
|
),
|
||||||
|
@ -53,7 +53,7 @@ class ShoppingCartScreen extends StatelessWidget {
|
||||||
builder: (context, _) => Column(
|
builder: (context, _) => Column(
|
||||||
children: [
|
children: [
|
||||||
for (var product in configuration.service.products)
|
for (var product in configuration.service.products)
|
||||||
configuration.productItemBuilder(
|
configuration.productItemBuilder!(
|
||||||
context,
|
context,
|
||||||
product,
|
product,
|
||||||
configuration,
|
configuration,
|
||||||
|
@ -63,8 +63,8 @@ class ShoppingCartScreen extends StatelessWidget {
|
||||||
// the bottom to make sure the last
|
// the bottom to make sure the last
|
||||||
// product(s) are not hidden by the bottom sheet.
|
// product(s) are not hidden by the bottom sheet.
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: configuration.confirmOrderButtonHeight +
|
height: configuration.confirmOrderButtonHeight! +
|
||||||
configuration.sumBottomSheetHeight,
|
configuration.sumBottomSheetHeight!,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -100,11 +100,11 @@ class _BottomSheet extends StatelessWidget {
|
||||||
ListenableBuilder(
|
ListenableBuilder(
|
||||||
listenable: configuration.service,
|
listenable: configuration.service,
|
||||||
builder: (BuildContext context, Widget? child) =>
|
builder: (BuildContext context, Widget? child) =>
|
||||||
configuration.sumBottomSheetBuilder(context, configuration),
|
configuration.sumBottomSheetBuilder!(context, configuration),
|
||||||
),
|
),
|
||||||
ListenableBuilder(
|
ListenableBuilder(
|
||||||
listenable: configuration.service,
|
listenable: configuration.service,
|
||||||
builder: (context, _) => configuration.confirmOrderButtonBuilder(
|
builder: (context, _) => configuration.confirmOrderButtonBuilder!(
|
||||||
context,
|
context,
|
||||||
configuration,
|
configuration,
|
||||||
configuration.onConfirmOrder,
|
configuration.onConfirmOrder,
|
||||||
|
|
|
@ -15,9 +15,6 @@ dependencies:
|
||||||
url: https://github.com/Iconica-Development/flutter_shopping
|
url: https://github.com/Iconica-Development/flutter_shopping
|
||||||
path: packages/flutter_shopping_interface
|
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:
|
||||||
|
|
Loading…
Reference in a new issue