mirror of
https://github.com/Iconica-Development/flutter_shopping.git
synced 2025-05-19 08:53:46 +02:00
feat(example): add amazon product page example
This commit is contained in:
parent
e201120bc2
commit
a38e97c68f
13 changed files with 763 additions and 0 deletions
53
example_amazon/.gitignore
vendored
Normal file
53
example_amazon/.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_amazon/README.md
Normal file
16
example_amazon/README.md
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# amazon
|
||||||
|
|
||||||
|
A new Flutter project.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
This project is a starting point for a Flutter application.
|
||||||
|
|
||||||
|
A few resources to get you started if this is your first Flutter project:
|
||||||
|
|
||||||
|
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
|
||||||
|
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
|
||||||
|
|
||||||
|
For help getting started with Flutter development, view the
|
||||||
|
[online documentation](https://docs.flutter.dev/), which offers tutorials,
|
||||||
|
samples, guidance on mobile development, and a full API reference.
|
28
example_amazon/analysis_options.yaml
Normal file
28
example_amazon/analysis_options.yaml
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
# This file configures the analyzer, which statically analyzes Dart code to
|
||||||
|
# check for errors, warnings, and lints.
|
||||||
|
#
|
||||||
|
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||||
|
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||||
|
# invoked from the command line by running `flutter analyze`.
|
||||||
|
|
||||||
|
# The following line activates a set of recommended lints for Flutter apps,
|
||||||
|
# packages, and plugins designed to encourage good coding practices.
|
||||||
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
|
linter:
|
||||||
|
# The lint rules applied to this project can be customized in the
|
||||||
|
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||||
|
# included above or to enable additional rules. A list of all available lints
|
||||||
|
# and their documentation is published at https://dart.dev/lints.
|
||||||
|
#
|
||||||
|
# Instead of disabling a lint rule for the entire project in the
|
||||||
|
# section below, it can also be suppressed for a single line of code
|
||||||
|
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||||
|
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||||
|
# producing the lint.
|
||||||
|
rules:
|
||||||
|
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||||
|
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||||
|
|
||||||
|
# Additional information about this file can be found at
|
||||||
|
# https://dart.dev/guides/language/analysis-options
|
22
example_amazon/lib/main.dart
Normal file
22
example_amazon/lib/main.dart
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import "package:amazon/src/routes.dart";
|
||||||
|
import "package:amazon/src/utils/theme.dart";
|
||||||
|
import "package:flutter/material.dart";
|
||||||
|
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
runApp(const ProviderScope(child: MyApp()));
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyApp extends HookConsumerWidget {
|
||||||
|
const MyApp({
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) => MaterialApp.router(
|
||||||
|
debugShowCheckedModeBanner: false,
|
||||||
|
restorationScopeId: "app",
|
||||||
|
theme: getTheme(),
|
||||||
|
routerConfig: ref.read(routerProvider),
|
||||||
|
);
|
||||||
|
}
|
361
example_amazon/lib/src/configuration/shopping_configuration.dart
Normal file
361
example_amazon/lib/src/configuration/shopping_configuration.dart
Normal file
|
@ -0,0 +1,361 @@
|
||||||
|
import "package:amazon/src/models/my_product.dart";
|
||||||
|
import "package:amazon/src/routes.dart";
|
||||||
|
import "package:amazon/src/services/category_service.dart";
|
||||||
|
import "package:flutter/material.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,
|
||||||
|
String? initialBuildShopId,
|
||||||
|
String? streetName,
|
||||||
|
) {
|
||||||
|
var theme = Theme.of(context);
|
||||||
|
|
||||||
|
return ProductPageScreen(
|
||||||
|
configuration: ProductPageConfiguration(
|
||||||
|
// (REQUIRED): List of shops that should be displayed
|
||||||
|
// If there is only one, make a list with just one shop.
|
||||||
|
shops: Future.value(getCategories()),
|
||||||
|
|
||||||
|
pagePadding: const EdgeInsets.symmetric(horizontal: 0, vertical: 4),
|
||||||
|
|
||||||
|
// (REQUIRED): Function to add a product to the cart
|
||||||
|
onAddToCart: (ProductPageProduct product) =>
|
||||||
|
productService.addProduct(product as MyProduct),
|
||||||
|
|
||||||
|
// (REQUIRED): Function to get the products for a shop
|
||||||
|
getProducts: (ProductPageShop shop) =>
|
||||||
|
Future<ProductPageContent>.value(
|
||||||
|
getShopContent(shop.id),
|
||||||
|
),
|
||||||
|
|
||||||
|
// (REQUIRED): Function to navigate to the shopping cart
|
||||||
|
onNavigateToShoppingCart: () => onCompleteProductPage(context),
|
||||||
|
|
||||||
|
shopSelectorStyle: ShopSelectorStyle.row,
|
||||||
|
|
||||||
|
navigateToShoppingCartBuilder: (context) => const SizedBox.shrink(),
|
||||||
|
|
||||||
|
bottomNavigationBar: BottomNavigationBar(
|
||||||
|
fixedColor: theme.primaryColor,
|
||||||
|
unselectedItemColor: Colors.black,
|
||||||
|
type: BottomNavigationBarType.fixed,
|
||||||
|
items: const [
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(Icons.home),
|
||||||
|
label: "Home",
|
||||||
|
),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(Icons.person_2_outlined),
|
||||||
|
label: "Profile",
|
||||||
|
),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(Icons.shopping_cart_outlined),
|
||||||
|
label: "Cart",
|
||||||
|
),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(Icons.menu),
|
||||||
|
label: "Menu",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
showSelectedLabels: false,
|
||||||
|
showUnselectedLabels: false,
|
||||||
|
onTap: (index) {
|
||||||
|
switch (index) {
|
||||||
|
case 0:
|
||||||
|
// context.go(homePage);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
context.go(FlutterShoppingPathRoutes.shoppingCart);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
productBuilder: (context, product) => Card(
|
||||||
|
elevation: 0,
|
||||||
|
color: const Color.fromARGB(255, 233, 233, 233),
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.zero,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Image.network(
|
||||||
|
product.imageUrl,
|
||||||
|
loadingBuilder: (context, child, loadingProgress) =>
|
||||||
|
loadingProgress == null
|
||||||
|
? child
|
||||||
|
: const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
errorBuilder: (context, error, stackTrace) =>
|
||||||
|
const Tooltip(
|
||||||
|
message: "Error loading image",
|
||||||
|
child: Icon(
|
||||||
|
Icons.error,
|
||||||
|
color: Colors.red,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 5,
|
||||||
|
child: ColoredBox(
|
||||||
|
color: theme.scaffoldBackgroundColor,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
product.name,
|
||||||
|
style: theme.textTheme.titleMedium,
|
||||||
|
maxLines: 3,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"4.5",
|
||||||
|
style: theme.textTheme.bodyMedium?.copyWith(
|
||||||
|
color: Colors.blue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Icon(Icons.star, color: Colors.orange),
|
||||||
|
const Icon(Icons.star, color: Colors.orange),
|
||||||
|
const Icon(Icons.star, color: Colors.orange),
|
||||||
|
const Icon(Icons.star, color: Colors.orange),
|
||||||
|
const Icon(Icons.star_half,
|
||||||
|
color: Colors.orange),
|
||||||
|
Text(
|
||||||
|
"(3)",
|
||||||
|
style: theme.textTheme.bodyMedium?.copyWith(
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"\$${product.price.toStringAsFixed(2)}",
|
||||||
|
style: theme.textTheme.titleMedium,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"Gratis bezorging door Amazon",
|
||||||
|
style: theme.textTheme.bodyMedium?.copyWith(
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
FilledButton(
|
||||||
|
onPressed: () {
|
||||||
|
productService.addProduct(product as MyProduct);
|
||||||
|
},
|
||||||
|
child: const Text("In winkelwagen"),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// (RECOMMENDED) The shop that is initially selected.
|
||||||
|
// Must be one of the shops in the [shops] list.
|
||||||
|
initialShopId: getCategories().first.id,
|
||||||
|
|
||||||
|
// (RECOMMENDED) Localizations for the product page.
|
||||||
|
localizations: const ProductPageLocalization(),
|
||||||
|
|
||||||
|
noContentBuilder: (context) => Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 128),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.warning,
|
||||||
|
size: 48,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"Geen producten gevonden",
|
||||||
|
style: theme.textTheme.titleLarge,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// (OPTIONAL) Appbar
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const SizedBox(
|
||||||
|
height: 40,
|
||||||
|
child: SearchBar(
|
||||||
|
hintText: "Search products",
|
||||||
|
leading: Icon(
|
||||||
|
Icons.search,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
trailing: [
|
||||||
|
Icon(
|
||||||
|
Icons.fit_screen_outlined,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
leading: IconButton(
|
||||||
|
icon: const Icon(Icons.arrow_back),
|
||||||
|
onPressed: () {
|
||||||
|
context.go(homePage);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
bottom: AppBar(
|
||||||
|
backgroundColor: const Color.fromRGBO(203, 237, 230, 1),
|
||||||
|
title: Row(
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.location_on_outlined),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
"Bestemming: ${streetName ?? "Mark - 1234AB Doetinchem Nederland"}",
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: theme.textTheme.titleMedium?.copyWith(
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
primary: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// (OPTIONAL): Initial build shop id that overrides the initialShop
|
||||||
|
initialBuildShopId: initialBuildShopId,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
// (REQUIRED): Shopping cart builder configuration
|
||||||
|
shoppingCartBuilder: (BuildContext context) => ShoppingCartScreen(
|
||||||
|
configuration: ShoppingCartConfig(
|
||||||
|
// (REQUIRED) product service instance:
|
||||||
|
productService: productService,
|
||||||
|
|
||||||
|
// (REQUIRED) product item builder:
|
||||||
|
productItemBuilder: (context, locale, product) => ListTile(
|
||||||
|
title: Text(product.name),
|
||||||
|
subtitle: Text(product.price.toStringAsFixed(2)),
|
||||||
|
leading: Image.network(
|
||||||
|
product.imageUrl,
|
||||||
|
errorBuilder: (context, error, stackTrace) => const Tooltip(
|
||||||
|
message: "Error loading image",
|
||||||
|
child: Icon(
|
||||||
|
Icons.error,
|
||||||
|
color: Colors.red,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
trailing: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.remove),
|
||||||
|
onPressed: () => productService.removeOneProduct(product),
|
||||||
|
),
|
||||||
|
Text("${product.quantity}"),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.add),
|
||||||
|
onPressed: () => productService.addProduct(product),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// (OPTIONAL/REQUIRED) on confirm order callback:
|
||||||
|
// Either use this callback or the placeOrderButtonBuilder.
|
||||||
|
onConfirmOrder: (products) => onCompleteShoppingCart(context),
|
||||||
|
|
||||||
|
// (RECOMMENDED) localizations:
|
||||||
|
localizations: const ShoppingCartLocalizations(),
|
||||||
|
|
||||||
|
/// (OPTIONAL) no content builder for when there are no products
|
||||||
|
/// in the shopping cart.
|
||||||
|
noContentBuilder: (context) => const Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 128),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.warning,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"Geen producten in winkelmandje",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// (OPTIONAL) custom appbar:
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text("Shopping Cart"),
|
||||||
|
leading: IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.arrow_back,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
context.go(FlutterShoppingPathRoutes.shop);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// (REQUIRED): Configuration on what to do when the user story is
|
||||||
|
// completed.
|
||||||
|
onCompleteUserStory: (BuildContext context) {
|
||||||
|
context.go(homePage);
|
||||||
|
},
|
||||||
|
|
||||||
|
// (RECOMMENDED) Handle processing of the order details. This function
|
||||||
|
// should return true if the order was processed successfully, otherwise
|
||||||
|
// false.
|
||||||
|
//
|
||||||
|
// If this function is not provided, it is assumed that the order is
|
||||||
|
// always processed successfully.
|
||||||
|
//
|
||||||
|
// Example use cases that could be implemented here:
|
||||||
|
// - Sending and storing the order on a server,
|
||||||
|
// - Processing payment (if the user decides to pay upfront).
|
||||||
|
// - And many more...
|
||||||
|
// onCompleteOrderDetails:
|
||||||
|
// (BuildContext context, OrderResult orderDetails) async {
|
||||||
|
// return true;
|
||||||
|
// },
|
||||||
|
);
|
8
example_amazon/lib/src/models/my_category.dart
Normal file
8
example_amazon/lib/src/models/my_category.dart
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import "package:flutter_product_page/flutter_product_page.dart";
|
||||||
|
|
||||||
|
class MyCategory extends ProductPageShop {
|
||||||
|
const MyCategory({
|
||||||
|
required super.id,
|
||||||
|
required super.name,
|
||||||
|
});
|
||||||
|
}
|
24
example_amazon/lib/src/models/my_product.dart
Normal file
24
example_amazon/lib/src/models/my_product.dart
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String category;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String imageUrl;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final double? discountPrice = 0.0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final bool hasDiscount = false;
|
||||||
|
}
|
31
example_amazon/lib/src/routes.dart
Normal file
31
example_amazon/lib/src/routes.dart
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import "package:amazon/src/configuration/shopping_configuration.dart";
|
||||||
|
import "package:amazon/src/ui/homepage.dart";
|
||||||
|
import "package:amazon/src/utils/go_router.dart";
|
||||||
|
import "package:flutter_shopping/flutter_shopping.dart";
|
||||||
|
import "package:go_router/go_router.dart";
|
||||||
|
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||||
|
|
||||||
|
const String homePage = "/";
|
||||||
|
|
||||||
|
final routerProvider = Provider<GoRouter>(
|
||||||
|
(ref) => GoRouter(
|
||||||
|
initialLocation: homePage,
|
||||||
|
routes: [
|
||||||
|
// Flutter Shopping Story Routes
|
||||||
|
...getShoppingStoryRoutes(
|
||||||
|
configuration: getFlutterShoppingConfiguration(),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Home Route
|
||||||
|
GoRoute(
|
||||||
|
name: "home",
|
||||||
|
path: homePage,
|
||||||
|
pageBuilder: (context, state) => buildScreenWithFadeTransition(
|
||||||
|
context: context,
|
||||||
|
state: state,
|
||||||
|
child: const Homepage(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
89
example_amazon/lib/src/services/category_service.dart
Normal file
89
example_amazon/lib/src/services/category_service.dart
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
import "package:amazon/src/models/my_category.dart";
|
||||||
|
import "package:amazon/src/models/my_product.dart";
|
||||||
|
import "package:flutter_product_page/flutter_product_page.dart";
|
||||||
|
|
||||||
|
Map<String, String> categories = {
|
||||||
|
"Electronics": "Electronica",
|
||||||
|
"Smart phones": "Telefoons",
|
||||||
|
"TV's": "TV's",
|
||||||
|
};
|
||||||
|
|
||||||
|
List<MyProduct> allProducts() => [
|
||||||
|
MyProduct(
|
||||||
|
id: "1",
|
||||||
|
name:
|
||||||
|
"Skar Audio Single 8\" Complete 1,200 Watt EVL Series Subwoofer Bass Package - Includes Loaded Enclosure with...",
|
||||||
|
price: 2.99,
|
||||||
|
category: categories["Electronics"]!,
|
||||||
|
imageUrl:
|
||||||
|
"https://m.media-amazon.com/images/I/710n3hnbfXL._AC_UY218_.jpg",
|
||||||
|
),
|
||||||
|
MyProduct(
|
||||||
|
id: "2",
|
||||||
|
name:
|
||||||
|
"Frameo 10.1 Inch WiFi Digital Picture Frame, 1280x800 HD IPS Touch Screen Photo Frame Electronic, 32GB Memory, Auto...",
|
||||||
|
price: 2.99,
|
||||||
|
category: categories["Electronics"]!,
|
||||||
|
imageUrl:
|
||||||
|
"https://m.media-amazon.com/images/I/61O+aorCp0L._AC_UY218_.jpg",
|
||||||
|
),
|
||||||
|
MyProduct(
|
||||||
|
id: "3",
|
||||||
|
name:
|
||||||
|
"STREBITO Electronics Precision Screwdriver Sets 142-Piece with 120 Bits Magnetic Repair Tool Kit for iPhone, MacBook,...",
|
||||||
|
price: 1.99,
|
||||||
|
category: categories["Electronics"]!,
|
||||||
|
imageUrl:
|
||||||
|
"https://m.media-amazon.com/images/I/81-C7lGtQsL._AC_UY218_.jpg",
|
||||||
|
),
|
||||||
|
MyProduct(
|
||||||
|
id: "4",
|
||||||
|
name:
|
||||||
|
"Samsung Galaxy A15 (SM-155M/DSN), 128GB 6GB RAM, Dual SIM, Factory Unlocked GSM, International Version (Wall...",
|
||||||
|
price: 1.99,
|
||||||
|
category: categories["Smart phones"]!,
|
||||||
|
imageUrl:
|
||||||
|
"https://m.media-amazon.com/images/I/51rp0nqaPoL._AC_UY218_.jpg",
|
||||||
|
),
|
||||||
|
MyProduct(
|
||||||
|
id: "5",
|
||||||
|
name:
|
||||||
|
"SAMSUNG Galaxy S24 Ultra Cell Phone, 512GB AI Smartphone, Unlocked Android, 50MP Zoom Camera, Long...",
|
||||||
|
price: 1.99,
|
||||||
|
category: categories["Smart phones"]!,
|
||||||
|
imageUrl:
|
||||||
|
"https://m.media-amazon.com/images/I/71ZoDT7a2wL._AC_UY218_.jpg",
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
List<MyCategory> getCategories() => <MyCategory>[
|
||||||
|
MyCategory(id: "1", name: categories["Electronics"]!),
|
||||||
|
MyCategory(id: "2", name: categories["Smart phones"]!),
|
||||||
|
MyCategory(id: "3", name: categories["TV's"]!),
|
||||||
|
const MyCategory(id: "4", name: "Monitoren"),
|
||||||
|
const MyCategory(id: "5", name: "Speakers"),
|
||||||
|
const MyCategory(id: "6", name: "Toetsenborden"),
|
||||||
|
];
|
||||||
|
|
||||||
|
ProductPageContent getShopContent(String shopId) {
|
||||||
|
var products = getProducts(shopId);
|
||||||
|
return ProductPageContent(
|
||||||
|
products: products,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<MyProduct> getProducts(String categoryId) {
|
||||||
|
if (categoryId == "1") {
|
||||||
|
return allProducts();
|
||||||
|
} else if (categoryId == "2") {
|
||||||
|
return allProducts()
|
||||||
|
.where((product) => product.category == categories["Smart phones"]!)
|
||||||
|
.toList();
|
||||||
|
} else if (categoryId == "3") {
|
||||||
|
return allProducts()
|
||||||
|
.where((product) => product.category == categories["TV's"]!)
|
||||||
|
.toList();
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
20
example_amazon/lib/src/ui/homepage.dart
Normal file
20
example_amazon/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(FlutterShoppingPathRoutes.shop),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
26
example_amazon/lib/src/utils/go_router.dart
Normal file
26
example_amazon/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,
|
||||||
|
);
|
43
example_amazon/lib/src/utils/theme.dart
Normal file
43
example_amazon/lib/src/utils/theme.dart
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
import "package:flutter/material.dart";
|
||||||
|
|
||||||
|
ThemeData getTheme() => ThemeData(
|
||||||
|
scaffoldBackgroundColor: const Color.fromRGBO(250, 249, 246, 1),
|
||||||
|
textTheme: const TextTheme(
|
||||||
|
labelMedium: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
titleMedium: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: Color.fromRGBO(60, 60, 59, 1),
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
inputDecorationTheme: const InputDecorationTheme(
|
||||||
|
fillColor: Colors.white,
|
||||||
|
),
|
||||||
|
colorScheme: const ColorScheme.light(
|
||||||
|
primary: Color.fromRGBO(161, 203, 211, 1),
|
||||||
|
secondary: Color.fromRGBO(221, 235, 238, 1),
|
||||||
|
surface: Color.fromRGBO(255, 255, 255, 1),
|
||||||
|
),
|
||||||
|
appBarTheme: const AppBarTheme(
|
||||||
|
backgroundColor: Color.fromRGBO(161, 220, 218, 1),
|
||||||
|
foregroundColor: Colors.black,
|
||||||
|
titleTextStyle: TextStyle(
|
||||||
|
fontSize: 28,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
filledButtonTheme: FilledButtonThemeData(
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor: WidgetStateProperty.all(
|
||||||
|
Colors.yellow,
|
||||||
|
),
|
||||||
|
foregroundColor: WidgetStateProperty.all(
|
||||||
|
Colors.black,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
42
example_amazon/pubspec.yaml
Normal file
42
example_amazon/pubspec.yaml
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
name: amazon
|
||||||
|
description: "A new Flutter project."
|
||||||
|
publish_to: 'none'
|
||||||
|
version: 1.0.0+1
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: '>=3.4.1 <4.0.0'
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
flutter_hooks: ^0.20.0
|
||||||
|
hooks_riverpod: ^2.1.1
|
||||||
|
go_router: 12.1.3
|
||||||
|
flutter_nested_categories:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_nested_categories
|
||||||
|
ref: 0.0.1
|
||||||
|
flutter_product_page:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_product_page
|
||||||
|
ref: 1.3.3
|
||||||
|
flutter_shopping_cart:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_shopping_cart
|
||||||
|
ref: 1.1.1
|
||||||
|
flutter_order_details:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_order_details
|
||||||
|
ref: 1.0.1
|
||||||
|
flutter_shopping:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_shopping
|
||||||
|
ref: 1.0.6
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
flutter_lints: ^3.0.0
|
||||||
|
|
||||||
|
flutter:
|
||||||
|
uses-material-design: true
|
Loading…
Reference in a new issue