mirror of
https://github.com/Iconica-Development/flutter_shopping.git
synced 2025-05-19 08:53:46 +02:00
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:
parent
bf3123aef4
commit
33184a3b01
23 changed files with 1003 additions and 14 deletions
157
README.md
157
README.md
|
@ -1,16 +1,161 @@
|
||||||
# flutter_shopping
|
# 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
|
## Usage
|
||||||
|
|
||||||
First, TODO...
|
For a detailed example you can see the [example](https://github.com/Iconica-Development/flutter_shopping/tree/main/example).
|
||||||
|
|
||||||
For a more detailed example you can see the [example](https://github.com/Iconica-Development/flutter_shopping/tree/main/example).
|
|
||||||
|
|
||||||
Or, you could run the example yourself:
|
Or, you could run the example yourself:
|
||||||
```
|
```
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
include: package:flutter_iconica_analysis/analysis_options.yaml
|
include: package:flutter_iconica_analysis/analysis_options.yaml
|
||||||
|
|
||||||
# Possible to overwrite the rules from the package
|
|
||||||
|
|
||||||
analyzer:
|
analyzer:
|
||||||
exclude:
|
exclude:
|
||||||
|
|
||||||
|
|
|
@ -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),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
165
example/lib/src/configuration/configuration.dart
Normal file
165
example/lib/src/configuration/configuration.dart
Normal 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;
|
||||||
|
},
|
||||||
|
);
|
26
example/lib/src/models/my_product.dart
Normal file
26
example/lib/src/models/my_product.dart
Normal 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;
|
||||||
|
}
|
8
example/lib/src/models/my_shop.dart
Normal file
8
example/lib/src/models/my_shop.dart
Normal 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,
|
||||||
|
});
|
||||||
|
}
|
31
example/lib/src/routes.dart
Normal file
31
example/lib/src/routes.dart
Normal 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(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
7
example/lib/src/services/order_service.dart
Normal file
7
example/lib/src/services/order_service.dart
Normal 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;
|
||||||
|
}
|
47
example/lib/src/services/shop_service.dart
Normal file
47
example/lib/src/services/shop_service.dart
Normal 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",
|
||||||
|
),
|
||||||
|
];
|
20
example/lib/src/ui/homepage.dart
Normal file
20
example/lib/src/ui/homepage.dart
Normal 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),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
26
example/lib/src/utils/go_router.dart
Normal file
26
example/lib/src/utils/go_router.dart
Normal 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,
|
||||||
|
);
|
32
example/lib/src/utils/theme.dart
Normal file
32
example/lib/src/utils/theme.dart
Normal 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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
|
@ -1,6 +1,6 @@
|
||||||
name: example
|
name: example
|
||||||
description: "Demonstrates how to use the flutter_shopping user story."
|
description: Demonstrates how to use the flutter_shopping package."
|
||||||
publish_to: 'none'
|
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||||
version: 1.0.0+1
|
version: 1.0.0+1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
|
@ -9,9 +9,30 @@ environment:
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
flutter_hooks: ^0.20.0
|
||||||
|
hooks_riverpod: ^2.1.1
|
||||||
|
go_router: 12.1.3
|
||||||
|
|
||||||
|
# Iconica packages
|
||||||
|
|
||||||
|
## Userstories
|
||||||
flutter_shopping:
|
flutter_shopping:
|
||||||
path: ../
|
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:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
7
lib/flutter_shopping.dart
Normal file
7
lib/flutter_shopping.dart
Normal 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";
|
69
lib/src/config/default_order_detail_configuration.dart
Normal file
69
lib/src/config/default_order_detail_configuration.dart
Normal 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),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
39
lib/src/config/flutter_shopping_configuration.dart
Normal file
39
lib/src/config/flutter_shopping_configuration.dart
Normal 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
28
lib/src/go_router.dart
Normal 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
17
lib/src/routes.dart
Normal 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";
|
||||||
|
}
|
|
@ -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,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
];
|
|
@ -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);
|
||||||
|
}
|
65
lib/src/widgets/default_order_failed_widget.dart
Normal file
65
lib/src/widgets/default_order_failed_widget.dart
Normal 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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
66
lib/src/widgets/default_order_succes_widget.dart
Normal file
66
lib/src/widgets/default_order_succes_widget.dart
Normal 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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,18 +9,19 @@ environment:
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
go_router: any
|
||||||
flutter_product_page:
|
flutter_product_page:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/Iconica-Development/flutter_product_page
|
url: https://github.com/Iconica-Development/flutter_product_page
|
||||||
ref: 1.0.0
|
ref: 1.1.0
|
||||||
flutter_shopping_cart:
|
flutter_shopping_cart:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/Iconica-Development/flutter_shopping_cart
|
url: https://github.com/Iconica-Development/flutter_shopping_cart
|
||||||
ref: 1.0.0
|
ref: 1.1.0
|
||||||
flutter_order_details:
|
flutter_order_details:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/Iconica-Development/flutter_order_details
|
url: https://github.com/Iconica-Development/flutter_order_details
|
||||||
ref: feat/v1.0.0
|
ref: 1.0.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
Loading…
Reference in a new issue