Feat/v1.0.0 (#1)

* feat: intial user story code

* fix: remove unused import

* fix: remove unused asset

* feat: readme

* feat: dart documentation

* fix: component versions

* fix: remove unused environment config

* fix: feedback
This commit is contained in:
Mark 2024-05-27 14:30:17 +02:00 committed by GitHub
parent bf3123aef4
commit 33184a3b01
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 1003 additions and 14 deletions

157
README.md
View file

@ -1,16 +1,161 @@
# flutter_shopping
This component contains TODO...
The flutter_shopping user-story allows you to create an user shopping flow within minutes of work. This user-story contains the ability to show products, shopping cart, gathering user information and order succes and failed screens.
## Features
## Setup
* TODO...
(1) Set up your `MyShop` model by extending from the `ProductPageShop` class. The most basic version looks like this:
```dart
class MyShop extends ProductPageShop {
const MyShop({
required super.id,
required super.name,
});
}
```
(2) Set up your `MyProduct` model by extending from `ShoppingCartProduct` and extending from the mixin `ProductPageProduct`, like this:
```dart
class MyProduct extends ShoppingCartProduct with ProductPageProduct {
MyProduct({
required super.id,
required super.name,
required super.price,
required this.category,
required this.imageUrl,
this.discountPrice,
this.hasDiscount = false,
});
@override
final String category;
@override
final String imageUrl;
@override
final double? discountPrice;
@override
final bool hasDiscount;
}
```
(3) Finally in your `routes.dart` import all the routes from the user-story:
```dart
...getShoppingStoryRoutes(
configuration: ...
),
```
(4) Create a new instantiation of the ProductService class:
```dart
final ProductService<MyProduct> productService = ProductService([]);
```
(5) Set up the `FlutterShoppingConfiguration`:
```dart
FlutterShoppingConfiguration(
// (REQUIRED): Shop builder configuration
shopBuilder: (BuildContext context) => ProductPageScreen(
configuration: ProductPageConfiguration(
// (REQUIRED): List of shops that should be displayed
// If there is only one, make a list with just one shop.
shops: getShops(),
// (REQUIRED): Function to add a product to the cart
onAddToCart: (ProductPageProduct product) =>
productService.addProduct(product as MyProduct),
// (REQUIRED): Function to get the products for a shop
getProducts: (String shopId) => Future<ProductPageContent>.value(
getShopContent(shopId),
),
// (REQUIRED): Function to navigate to the shopping cart
onNavigateToShoppingCart: () => onCompleteProductPage(context),
// (RECOMMENDED) Function that returns the description for a
// product that is on sale.
getDiscountDescription: (ProductPageProduct 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: (shopId) => productService.clear(),
// (RECOMMENDED) The shop that is initially selected.
// Must be one of the shops in the [shops] list.
initialShop: getShops().first,
// (RECOMMENDED) Localizations for the product page.
localizations: const ProductPageLocalization(),
// (OPTIONAL) Appbar
appBar: ...
),
),
// (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) => ...
// (OPTIONAL/REQUIRED) on confirm order callback:
// Either use this callback or the placeOrderButtonBuilder.
onConfirmOrder: (products) => onCompleteShoppingCart(context),
// (RECOMMENDED) localizations:
localizations: const ShoppingCartLocalizations(),
// (OPTIONAL) custom appbar:
appBar: ...
),
),
// (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 {
...
},
)
```
For more information about the component specific items please take a look at their repositories:
- [flutter_product_page](https://github.com/Iconica-Development/flutter_product_page/)
- [flutter_shopping_cart](https://github.com/Iconica-Development/flutter_shopping_cart)
- [flutter_order_details](https://github.com/Iconica-Development/flutter_order_details)
## Usage
First, TODO...
For a more detailed example you can see the [example](https://github.com/Iconica-Development/flutter_shopping/tree/main/example).
For a detailed example you can see the [example](https://github.com/Iconica-Development/flutter_shopping/tree/main/example).
Or, you could run the example yourself:
```

View file

@ -1,7 +1,5 @@
include: package:flutter_iconica_analysis/analysis_options.yaml
# Possible to overwrite the rules from the package
analyzer:
exclude:

View file

@ -1 +1,22 @@
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),
);
}

View file

@ -0,0 +1,165 @@
import "package:example/src/models/my_product.dart";
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_order_details/flutter_order_details.dart";
import "package:flutter_product_page/flutter_product_page.dart";
import "package:flutter_shopping/flutter_shopping.dart";
import "package:flutter_shopping_cart/flutter_shopping_cart.dart";
import "package:go_router/go_router.dart";
// (REQUIRED): Create your own instance of the ProductService.
final ProductService<MyProduct> productService = ProductService([]);
FlutterShoppingConfiguration getFlutterShoppingConfiguration() =>
FlutterShoppingConfiguration(
// (REQUIRED): Shop builder configuration
shopBuilder: (BuildContext context) => ProductPageScreen(
configuration: ProductPageConfiguration(
// (REQUIRED): List of shops that should be displayed
// If there is only one, make a list with just one shop.
shops: getShops(),
// (REQUIRED): Function to add a product to the cart
onAddToCart: (ProductPageProduct product) =>
productService.addProduct(product as MyProduct),
// (REQUIRED): Function to get the products for a shop
getProducts: (String shopId) => Future<ProductPageContent>.value(
getShopContent(shopId),
),
// (REQUIRED): Function to navigate to the shopping cart
onNavigateToShoppingCart: () => 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: (ProductPageProduct 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: (shopId) => productService.clear(),
// (RECOMMENDED) The shop that is initially selected.
// Must be one of the shops in the [shops] list.
initialShop: getShops().first,
// (RECOMMENDED) Localizations for the product page.
localizations: const ProductPageLocalization(),
// (OPTIONAL) Appbar
appBar: AppBar(
title: const Text("Shop"),
leading: IconButton(
icon: const Icon(
Icons.arrow_back,
color: Colors.white,
),
onPressed: () {
context.go(homePage);
},
),
),
),
),
// (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) => 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) title above product list:
title: "Products",
// (OPTIONAL) custom appbar:
appBar: AppBar(
title: const Text("Shopping Cart"),
leading: IconButton(
icon: const Icon(
Icons.arrow_back,
color: Colors.white,
),
onPressed: () {
context.go(FlutterShoppingRoutes.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;
},
);

View file

@ -0,0 +1,26 @@
import "package:flutter_product_page/flutter_product_page.dart";
import "package:flutter_shopping_cart/flutter_shopping_cart.dart";
class MyProduct extends ShoppingCartProduct with ProductPageProduct {
MyProduct({
required super.id,
required super.name,
required super.price,
required this.category,
required this.imageUrl,
this.discountPrice,
this.hasDiscount = false,
});
@override
final String category;
@override
final String imageUrl;
@override
final double? discountPrice;
@override
final bool hasDiscount;
}

View file

@ -0,0 +1,8 @@
import "package:flutter_product_page/flutter_product_page.dart";
class MyShop extends ProductPageShop {
const MyShop({
required super.id,
required super.name,
});
}

View file

@ -0,0 +1,31 @@
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(),
),
),
],
),
);

View file

@ -0,0 +1,7 @@
import "package:example/src/models/my_product.dart";
import "package:flutter_order_details/flutter_order_details.dart";
/// Example implementation of storing an order in a database.
void storeOrderInDatabase(List<MyProduct> products, OrderResult result) {
return;
}

View file

@ -0,0 +1,47 @@
import "package:example/src/models/my_product.dart";
import "package:example/src/models/my_shop.dart";
import "package:flutter_product_page/flutter_product_page.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<MyProduct> getProducts(String shopId) => <MyProduct>[
MyProduct(
id: "1",
name: "White bread",
price: 2.99,
category: "Loaves",
imageUrl: "https://via.placeholder.com/150",
hasDiscount: true,
discountPrice: 1.99,
),
MyProduct(
id: "2",
name: "Brown bread",
price: 2.99,
category: "Loaves",
imageUrl: "https://via.placeholder.com/150",
),
MyProduct(
id: "3",
name: "Cheese sandwich",
price: 1.99,
category: "Sandwiches",
imageUrl: "https://via.placeholder.com/150",
),
];

View file

@ -0,0 +1,20 @@
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(FlutterShoppingRoutes.shop),
),
),
),
);
}

View file

@ -0,0 +1,26 @@
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,
);

View file

@ -0,0 +1,32 @@
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,
),
),
);

View file

@ -1,6 +1,6 @@
name: example
description: "Demonstrates how to use the flutter_shopping user story."
publish_to: 'none'
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+1
environment:
@ -9,9 +9,30 @@ environment:
dependencies:
flutter:
sdk: flutter
flutter_hooks: ^0.20.0
hooks_riverpod: ^2.1.1
go_router: 12.1.3
# Iconica packages
## Userstories
flutter_shopping:
path: ../
## Normal Packages
flutter_product_page:
git:
url: https://github.com/Iconica-Development/flutter_product_page
ref: 1.1.0
flutter_shopping_cart:
git:
url: https://github.com/Iconica-Development/flutter_shopping_cart
ref: 1.1.0
flutter_order_details:
git:
url: https://github.com/Iconica-Development/flutter_order_details
ref: 1.0.0
dev_dependencies:
flutter_test:
sdk: flutter
@ -31,4 +52,4 @@ flutter:
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# style: italic

View file

@ -0,0 +1,7 @@
/// Flutter Shopping
library flutter_shopping;
export "package:flutter_shopping/src/config/flutter_shopping_configuration.dart";
export "package:flutter_shopping/src/routes.dart";
export "package:flutter_shopping/src/user_stores/flutter_shopping_userstory_go_router.dart";
export "package:flutter_shopping/src/user_stores/flutter_shopping_userstory_navigation.dart";

View file

@ -0,0 +1,69 @@
import "package:flutter/material.dart";
import "package:flutter_order_details/flutter_order_details.dart";
import "package:flutter_shopping/flutter_shopping.dart";
import "package:go_router/go_router.dart";
/// Default order detail configuration for the app.
/// This configuration is used to create the order detail page.
OrderDetailConfiguration getDefaultOrderDetailConfiguration(
BuildContext context,
FlutterShoppingConfiguration configuration,
) =>
OrderDetailConfiguration(
steps: [
OrderDetailStep(
formKey: GlobalKey<FormState>(),
stepName: "Basic Information",
fields: [
OrderTextInput(
title: "First name",
outputKey: "first_name",
textController: TextEditingController(),
),
OrderTextInput(
title: "Last name",
outputKey: "last_name",
textController: TextEditingController(),
),
OrderEmailInput(
title: "Your email address",
outputKey: "email",
textController: TextEditingController(),
subtitle: "* We will send your order confirmation here",
hint: "your_email@mail.com",
),
],
),
OrderDetailStep(
formKey: GlobalKey<FormState>(),
stepName: "Address Information",
fields: [
OrderAddressInput(
title: "Your address",
outputKey: "address",
textController: TextEditingController(),
),
],
),
OrderDetailStep(
formKey: GlobalKey<FormState>(),
stepName: "Payment Information",
fields: [
OrderChoiceInput(
title: "Payment option",
outputKey: "payment_option",
items: ["Pay now", "Pay later"],
),
],
),
],
onCompleted: (OrderResult result) async =>
onCompleteOrderDetails(context, configuration, result),
appBar: AppBar(
title: const Text("Order Details"),
leading: IconButton(
icon: const Icon(Icons.close, color: Colors.white),
onPressed: () => context.go(FlutterShoppingRoutes.shoppingCart),
),
),
);

View file

@ -0,0 +1,39 @@
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) 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;
}

28
lib/src/go_router.dart Normal file
View file

@ -0,0 +1,28 @@
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,
);

17
lib/src/routes.dart Normal file
View file

@ -0,0 +1,17 @@
/// All the routes used in the user-story.
mixin FlutterShoppingRoutes {
/// 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";
}

View file

@ -0,0 +1,97 @@
import "package:flutter/material.dart";
import "package:flutter_order_details/flutter_order_details.dart";
import "package:flutter_shopping/flutter_shopping.dart";
import "package:flutter_shopping/src/config/default_order_detail_configuration.dart";
import "package:flutter_shopping/src/go_router.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: "shop",
path: FlutterShoppingRoutes.shop,
pageBuilder: (BuildContext context, GoRouterState state) =>
buildScreenWithFadeTransition(
context: context,
state: state,
child: configuration.shopBuilder(context),
),
),
GoRoute(
name: "shoppingCart",
path: FlutterShoppingRoutes.shoppingCart,
pageBuilder: (BuildContext context, GoRouterState state) =>
buildScreenWithFadeTransition(
context: context,
state: state,
child: configuration.shoppingCartBuilder(context),
),
),
GoRoute(
name: "orderDetails",
path: FlutterShoppingRoutes.orderDetails,
pageBuilder: (BuildContext context, GoRouterState state) {
if (configuration.orderDetailsBuilder != null) {
return buildScreenWithFadeTransition(
context: context,
state: state,
child: configuration.orderDetailsBuilder!(context),
);
}
return buildScreenWithFadeTransition(
context: context,
state: state,
child: OrderDetailScreen(
configuration:
getDefaultOrderDetailConfiguration(context, configuration),
),
);
},
),
GoRoute(
name: "orderSuccess",
path: FlutterShoppingRoutes.orderSuccess,
pageBuilder: (BuildContext context, GoRouterState state) {
if (configuration.orderSuccessBuilder != null) {
return buildScreenWithFadeTransition(
context: context,
state: state,
child: configuration.orderSuccessBuilder!(context),
);
}
return buildScreenWithFadeTransition(
context: context,
state: state,
child: DefaultOrderSucces(configuration: configuration),
);
},
),
GoRoute(
name: "orderFailed",
path: FlutterShoppingRoutes.orderFailed,
pageBuilder: (BuildContext context, GoRouterState state) {
if (configuration.orderFailedBuilder != null) {
return buildScreenWithFadeTransition(
context: context,
state: state,
child: configuration.orderFailedBuilder!(context),
);
}
return buildScreenWithFadeTransition(
context: context,
state: state,
child: DefaultOrderFailed(
configuration: configuration,
),
);
},
),
];

View file

@ -0,0 +1,53 @@
import "package:flutter/material.dart";
import "package:flutter_order_details/flutter_order_details.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(FlutterShoppingRoutes.orderSuccess);
} else {
go(FlutterShoppingRoutes.orderFailed);
}
}
/// Default on complete shopping cart function.
///
/// You can create your own implementation if you decide to use a different
/// approach.
void onCompleteShoppingCart(
BuildContext context,
) {
context.go(FlutterShoppingRoutes.orderDetails);
}
/// Default on complete product page function.
///
/// You can create your own implementation if you decide to use a different
/// approach.
void onCompleteProductPage(
BuildContext context,
) {
context.go(FlutterShoppingRoutes.shoppingCart);
}

View file

@ -0,0 +1,65 @@
import "package:flutter/material.dart";
import "package:flutter_shopping/flutter_shopping.dart";
/// Default order failed widget.
class DefaultOrderFailed extends StatelessWidget {
/// Constructor for the DefaultOrderFailed.
const DefaultOrderFailed({
required this.configuration,
super.key,
});
/// Configuration for the user-story.
final FlutterShoppingConfiguration configuration;
@override
Widget build(BuildContext context) {
var theme = Theme.of(context);
var finishOrderButton = FilledButton(
onPressed: () => configuration.onCompleteUserStory(context),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 32.0,
vertical: 8.0,
),
child: Text("Go back".toUpperCase()),
),
);
var content = Column(
children: [
const Spacer(),
const Icon(
Icons.error,
size: 100,
color: Colors.red,
),
const SizedBox(height: 16),
Text(
"Uh oh.",
style: theme.textTheme.titleLarge,
),
const SizedBox(height: 32),
Text(
"It seems that something went wrong.",
style: theme.textTheme.bodyMedium,
),
Text(
"Please try again later.",
style: theme.textTheme.bodyMedium,
),
const Spacer(),
finishOrderButton,
],
);
return Scaffold(
body: SafeArea(
child: Center(
child: content,
),
),
);
}
}

View file

@ -0,0 +1,66 @@
import "package:flutter/material.dart";
import "package:flutter_shopping/flutter_shopping.dart";
/// Default order success widget.
class DefaultOrderSucces extends StatelessWidget {
/// Constructor for the DefaultOrderSucces.
const DefaultOrderSucces({
required this.configuration,
super.key,
});
/// Configuration for the user-story.
final FlutterShoppingConfiguration configuration;
@override
Widget build(BuildContext context) {
var theme = Theme.of(context);
var finishOrderButton = FilledButton(
onPressed: () => configuration.onCompleteUserStory(context),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 32.0,
vertical: 8.0,
),
child: Text("Finish Order".toUpperCase()),
),
);
var content = Column(
children: [
const Spacer(),
Text("#123456", style: theme.textTheme.titleLarge),
const SizedBox(height: 16),
Text(
"Order Succesfully Placed!",
style: theme.textTheme.titleLarge,
),
Text(
"Thank you for your order!",
style: theme.textTheme.bodyMedium,
),
const SizedBox(height: 16),
Text(
"Your order will be delivered soon.",
style: theme.textTheme.bodyMedium,
),
const SizedBox(height: 16),
Text(
"Do you want to order again?",
style: theme.textTheme.bodyMedium,
),
const Spacer(),
finishOrderButton,
],
);
return Scaffold(
body: SafeArea(
child: Center(
child: content,
),
),
);
}
}

View file

@ -9,18 +9,19 @@ environment:
dependencies:
flutter:
sdk: flutter
go_router: any
flutter_product_page:
git:
url: https://github.com/Iconica-Development/flutter_product_page
ref: 1.0.0
ref: 1.1.0
flutter_shopping_cart:
git:
url: https://github.com/Iconica-Development/flutter_shopping_cart
ref: 1.0.0
ref: 1.1.0
flutter_order_details:
git:
url: https://github.com/Iconica-Development/flutter_order_details
ref: feat/v1.0.0
ref: 1.0.0
dev_dependencies:
flutter_test: