mirror of
https://github.com/Iconica-Development/flutter_shopping.git
synced 2025-05-19 00:43:45 +02:00
feat: intial user story code
This commit is contained in:
parent
3fc55190cd
commit
a0fe3b6f67
25 changed files with 965 additions and 2 deletions
53
example/.gitignore
vendored
Normal file
53
example/.gitignore
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
# 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/
|
16
example/README.md
Normal file
16
example/README.md
Normal file
|
@ -0,0 +1,16 @@
|
|||
# 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.
|
7
example/analysis_options.yaml
Normal file
7
example/analysis_options.yaml
Normal file
|
@ -0,0 +1,7 @@
|
|||
include: package:flutter_iconica_analysis/analysis_options.yaml
|
||||
|
||||
analyzer:
|
||||
exclude:
|
||||
|
||||
linter:
|
||||
rules:
|
8
example/environment_config.yaml
Normal file
8
example/environment_config.yaml
Normal file
|
@ -0,0 +1,8 @@
|
|||
environment_config:
|
||||
dotenv_path: dotenv
|
||||
path: env_config.dart
|
||||
fields:
|
||||
STRIPE_PUBLISHABLE_KEY:
|
||||
dotenv: true
|
||||
config_field: false
|
||||
default: ""
|
22
example/lib/main.dart
Normal file
22
example/lib/main.dart
Normal file
|
@ -0,0 +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/main.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/main.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",
|
||||
),
|
||||
];
|
22
example/lib/src/ui/homepage.dart
Normal file
22
example/lib/src/ui/homepage.dart
Normal file
|
@ -0,0 +1,22 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:flutter_shopping/main.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,
|
||||
);
|
31
example/lib/src/utils/theme.dart
Normal file
31
example/lib/src/utils/theme.dart
Normal file
|
@ -0,0 +1,31 @@
|
|||
import "package:flutter/material.dart";
|
||||
|
||||
ThemeData getTheme() => ThemeData(
|
||||
textTheme: const TextTheme(
|
||||
labelMedium: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Color.fromRGBO(0, 0, 0, 1),
|
||||
),
|
||||
titleMedium: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Color.fromRGBO(60, 60, 59, 1),
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
inputDecorationTheme: const InputDecorationTheme(
|
||||
fillColor: Color.fromRGBO(255, 255, 255, 1),
|
||||
),
|
||||
colorScheme: const ColorScheme.light(
|
||||
primary: Color.fromRGBO(64, 87, 122, 1),
|
||||
secondary: Color.fromRGBO(255, 255, 255, 1),
|
||||
surface: Color.fromRGBO(250, 249, 246, 1),
|
||||
),
|
||||
appBarTheme: const AppBarTheme(
|
||||
backgroundColor: Color.fromRGBO(64, 87, 122, 1),
|
||||
titleTextStyle: TextStyle(
|
||||
fontSize: 28,
|
||||
color: Color.fromRGBO(255, 255, 255, 1),
|
||||
),
|
||||
),
|
||||
);
|
56
example/pubspec.yaml
Normal file
56
example/pubspec.yaml
Normal file
|
@ -0,0 +1,56 @@
|
|||
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+1
|
||||
|
||||
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
|
||||
|
||||
# Iconica packages
|
||||
|
||||
## Userstories
|
||||
flutter_shopping:
|
||||
path: ../
|
||||
|
||||
## Normal Packages
|
||||
flutter_product_page:
|
||||
git:
|
||||
url: https://github.com/Iconica-Development/flutter_product_page
|
||||
ref: fix/missing-features
|
||||
flutter_shopping_cart:
|
||||
git:
|
||||
url: https://github.com/Iconica-Development/flutter_shopping_cart
|
||||
ref: fix/missing-features
|
||||
flutter_order_details:
|
||||
git:
|
||||
url: https://github.com/Iconica-Development/flutter_order_details
|
||||
ref: feat/v1.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:
|
||||
- dotenv
|
||||
# - 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
|
4
lib/main.dart
Normal file
4
lib/main.dart
Normal file
|
@ -0,0 +1,4 @@
|
|||
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";
|
70
lib/src/config/default_order_detail_configuration.dart
Normal file
70
lib/src/config/default_order_detail_configuration.dart
Normal file
|
@ -0,0 +1,70 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:flutter_order_details/flutter_order_details.dart";
|
||||
import "package:flutter_shopping/main.dart";
|
||||
import "package:flutter_shopping/src/user_stores/flutter_shopping_userstory_navigation.dart";
|
||||
import "package:go_router/go_router.dart";
|
||||
|
||||
/// TODO
|
||||
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: ""
|
||||
),
|
||||
],
|
||||
),
|
||||
OrderDetailStep(
|
||||
formKey: GlobalKey<FormState>(),
|
||||
stepName: "Adress Information",
|
||||
fields: [
|
||||
OrderAdresInput(
|
||||
title: "Your adress",
|
||||
outputKey: "adres",
|
||||
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 {
|
||||
await 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),
|
||||
),
|
||||
),
|
||||
);
|
46
lib/src/config/flutter_shopping_configuration.dart
Normal file
46
lib/src/config/flutter_shopping_configuration.dart
Normal file
|
@ -0,0 +1,46 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:flutter_order_details/flutter_order_details.dart";
|
||||
|
||||
/// TODO
|
||||
class FlutterShoppingConfiguration {
|
||||
/// TODO
|
||||
const FlutterShoppingConfiguration({
|
||||
required this.shopBuilder,
|
||||
required this.shoppingCartBuilder,
|
||||
required this.onCompleteUserStory,
|
||||
this.showOrderDetails = false,
|
||||
this.orderDetailsBuilder,
|
||||
this.onCompleteOrderDetails,
|
||||
this.orderSuccessBuilder,
|
||||
this.orderFailedBuilder,
|
||||
}) : assert(
|
||||
showOrderDetails && orderDetailsBuilder != null ||
|
||||
!showOrderDetails && orderDetailsBuilder == null,
|
||||
"showOrderDetails and orderDetailsBuilder must be both set or unset.",
|
||||
);
|
||||
|
||||
/// TODO
|
||||
final Widget Function(BuildContext context) shopBuilder;
|
||||
|
||||
/// TODO
|
||||
final Widget Function(BuildContext context) shoppingCartBuilder;
|
||||
|
||||
/// TODO
|
||||
final Function(BuildContext context) onCompleteUserStory;
|
||||
|
||||
/// TODO
|
||||
final bool showOrderDetails;
|
||||
|
||||
/// TODO
|
||||
final Widget Function(BuildContext context)? orderDetailsBuilder;
|
||||
|
||||
/// Allows you to execute actions before
|
||||
final Future<bool> Function(BuildContext context, OrderResult result)?
|
||||
onCompleteOrderDetails;
|
||||
|
||||
/// TODO
|
||||
final Widget Function(BuildContext context)? orderSuccessBuilder;
|
||||
|
||||
/// TODO
|
||||
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";
|
||||
|
||||
/// TODO
|
||||
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),
|
||||
);
|
||||
|
||||
/// TODO
|
||||
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 @@
|
|||
/// TODO
|
||||
mixin FlutterShoppingRoutes {
|
||||
/// TODO
|
||||
static const String shop = "/shop";
|
||||
|
||||
/// TODO
|
||||
static const String shoppingCart = "/shopping-cart";
|
||||
|
||||
/// TODO
|
||||
static const String orderDetails = "/order-details";
|
||||
|
||||
/// TODO
|
||||
static const String orderSuccess = "/order-success";
|
||||
|
||||
/// TODO
|
||||
static const String orderFailed = "/order-failed";
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:flutter_order_details/flutter_order_details.dart";
|
||||
import "package:flutter_shopping/main.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";
|
||||
|
||||
/// TODO
|
||||
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.showOrderDetails &&
|
||||
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,43 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:flutter_order_details/flutter_order_details.dart";
|
||||
import "package:flutter_shopping/main.dart";
|
||||
import "package:go_router/go_router.dart";
|
||||
|
||||
/// TODO
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO
|
||||
void onCompleteShoppingCart(
|
||||
BuildContext context,
|
||||
) {
|
||||
context.go(FlutterShoppingRoutes.orderDetails);
|
||||
}
|
||||
|
||||
/// TODO
|
||||
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/main.dart";
|
||||
|
||||
/// TODO
|
||||
class DefaultOrderFailed extends StatelessWidget {
|
||||
/// TODO
|
||||
const DefaultOrderFailed({
|
||||
required this.configuration,
|
||||
super.key,
|
||||
});
|
||||
|
||||
/// TODO
|
||||
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/main.dart";
|
||||
|
||||
/// TODO
|
||||
class DefaultOrderSucces extends StatelessWidget {
|
||||
/// TODO
|
||||
const DefaultOrderSucces({
|
||||
required this.configuration,
|
||||
super.key,
|
||||
});
|
||||
|
||||
/// TODO
|
||||
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,14 +9,15 @@ 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: fix/missing-features
|
||||
flutter_shopping_cart:
|
||||
git:
|
||||
url: https://github.com/Iconica-Development/flutter_shopping_cart
|
||||
ref: 1.0.0
|
||||
ref: fix/missing-features
|
||||
flutter_order_details:
|
||||
git:
|
||||
url: https://github.com/Iconica-Development/flutter_order_details
|
||||
|
|
Loading…
Reference in a new issue