mirror of
https://github.com/Iconica-Development/flutter_shopping.git
synced 2025-05-19 17:03:45 +02:00
feat: remove listenablebuilder from product page
This commit is contained in:
parent
2bf42c4acb
commit
0e7e2ff0e5
8 changed files with 326 additions and 220 deletions
|
@ -1,7 +1,6 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter_order_details/flutter_order_details.dart";
|
import "package:flutter_order_details/flutter_order_details.dart";
|
||||||
|
|
||||||
|
|
||||||
/// Default next button for the order details page.
|
/// Default next button for the order details page.
|
||||||
class DefaultNextButton extends StatelessWidget {
|
class DefaultNextButton extends StatelessWidget {
|
||||||
/// Constructor for the default next button for the order details page.
|
/// Constructor for the default next button for the order details page.
|
||||||
|
@ -15,10 +14,13 @@ class DefaultNextButton extends StatelessWidget {
|
||||||
|
|
||||||
/// Configuration for the order details page.
|
/// Configuration for the order details page.
|
||||||
final OrderDetailConfiguration configuration;
|
final OrderDetailConfiguration configuration;
|
||||||
|
|
||||||
/// Controller for the form.
|
/// Controller for the form.
|
||||||
final FlutterFormController controller;
|
final FlutterFormController controller;
|
||||||
|
|
||||||
/// Current step in the form.
|
/// Current step in the form.
|
||||||
final int currentStep;
|
final int currentStep;
|
||||||
|
|
||||||
/// Whether the form is checking pages.
|
/// Whether the form is checking pages.
|
||||||
final bool checkingPages;
|
final bool checkingPages;
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import "package:collection/collection.dart";
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter_product_page/flutter_product_page.dart";
|
import "package:flutter_product_page/flutter_product_page.dart";
|
||||||
import "package:flutter_product_page/src/services/category_service.dart";
|
import "package:flutter_product_page/src/services/category_service.dart";
|
||||||
|
@ -5,41 +6,44 @@ import "package:flutter_product_page/src/widgets/defaults/default_appbar.dart";
|
||||||
import "package:flutter_product_page/src/widgets/defaults/default_error.dart";
|
import "package:flutter_product_page/src/widgets/defaults/default_error.dart";
|
||||||
import "package:flutter_product_page/src/widgets/defaults/default_no_content.dart";
|
import "package:flutter_product_page/src/widgets/defaults/default_no_content.dart";
|
||||||
import "package:flutter_product_page/src/widgets/defaults/default_shopping_cart_button.dart";
|
import "package:flutter_product_page/src/widgets/defaults/default_shopping_cart_button.dart";
|
||||||
|
import "package:flutter_product_page/src/widgets/defaults/selected_categories.dart";
|
||||||
import "package:flutter_product_page/src/widgets/shop_selector.dart";
|
import "package:flutter_product_page/src/widgets/shop_selector.dart";
|
||||||
import "package:flutter_product_page/src/widgets/weekly_discount.dart";
|
import "package:flutter_product_page/src/widgets/weekly_discount.dart";
|
||||||
import "package:flutter_shopping_interface/flutter_shopping_interface.dart";
|
import "package:flutter_shopping_interface/flutter_shopping_interface.dart";
|
||||||
|
|
||||||
/// A page that displays products.
|
/// A page that displays products.
|
||||||
class ProductPageScreen extends StatelessWidget {
|
class ProductPageScreen extends StatefulWidget {
|
||||||
/// Constructor for the product page.
|
/// Constructor for the product page.
|
||||||
const ProductPageScreen({
|
const ProductPageScreen({
|
||||||
required this.configuration,
|
required this.configuration,
|
||||||
this.initialBuildShopId,
|
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Configuration for the product page.
|
/// Configuration for the product page.
|
||||||
final ProductPageConfiguration configuration;
|
final ProductPageConfiguration configuration;
|
||||||
|
|
||||||
/// An optional initial shop ID to select. This overrides the initialShopId
|
@override
|
||||||
/// from the configuration.
|
State<ProductPageScreen> createState() => _ProductPageScreenState();
|
||||||
final String? initialBuildShopId;
|
}
|
||||||
|
|
||||||
|
class _ProductPageScreenState extends State<ProductPageScreen> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => Scaffold(
|
Widget build(BuildContext context) => Scaffold(
|
||||||
appBar: configuration.appBarBuilder?.call(context) ??
|
appBar: widget.configuration.appBarBuilder?.call(context) ??
|
||||||
DefaultAppbar(
|
DefaultAppbar(
|
||||||
configuration: configuration,
|
configuration: widget.configuration,
|
||||||
),
|
),
|
||||||
bottomNavigationBar: configuration.bottomNavigationBar,
|
bottomNavigationBar: widget.configuration.bottomNavigationBar,
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: configuration.pagePadding,
|
padding: widget.configuration.pagePadding,
|
||||||
child: FutureBuilder(
|
child: FutureBuilder(
|
||||||
// ignore: discarded_futures
|
// ignore: discarded_futures
|
||||||
future: configuration.shops(),
|
future: widget.configuration.shops(),
|
||||||
builder: (BuildContext context, AsyncSnapshot data) {
|
builder: (context, snapshot) {
|
||||||
if (data.connectionState == ConnectionState.waiting) {
|
List<Shop>? shops;
|
||||||
|
|
||||||
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
return const Column(
|
return const Column(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
@ -49,106 +53,47 @@ class ProductPageScreen extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.hasError) {
|
if (snapshot.hasError) {
|
||||||
return configuration.errorBuilder?.call(
|
return widget.configuration.errorBuilder?.call(
|
||||||
context,
|
context,
|
||||||
data.error,
|
snapshot.error,
|
||||||
) ??
|
) ??
|
||||||
DefaultError(
|
DefaultError(
|
||||||
error: data.error,
|
error: snapshot.error,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Shop>? shops = data.data;
|
shops = snapshot.data;
|
||||||
|
|
||||||
if (shops == null || shops.isEmpty) {
|
if (shops == null || shops.isEmpty) {
|
||||||
return configuration.errorBuilder?.call(
|
return widget.configuration.errorBuilder?.call(
|
||||||
context,
|
context,
|
||||||
data.error,
|
snapshot.error,
|
||||||
) ??
|
) ??
|
||||||
DefaultError(error: data.error);
|
DefaultError(error: snapshot.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (initialBuildShopId != null) {
|
if (widget.configuration.initialShopId != null) {
|
||||||
Shop? initialShop;
|
var initialShop = shops.firstWhereOrNull(
|
||||||
|
(shop) => shop.id == widget.configuration.initialShopId,
|
||||||
for (var shop in shops) {
|
);
|
||||||
if (shop.id == initialBuildShopId) {
|
if (initialShop != null) {
|
||||||
initialShop = shop;
|
widget.configuration.shoppingService.shopService.selectShop(
|
||||||
break;
|
initialShop,
|
||||||
}
|
);
|
||||||
}
|
|
||||||
|
|
||||||
configuration.shoppingService.shopService
|
|
||||||
.selectShop(initialShop ?? shops.first);
|
|
||||||
} else if (configuration.initialShopId != null) {
|
|
||||||
Shop? initialShop;
|
|
||||||
|
|
||||||
for (var shop in shops) {
|
|
||||||
if (shop.id == configuration.initialShopId) {
|
|
||||||
initialShop = shop;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
configuration.shoppingService.shopService
|
|
||||||
.selectShop(initialShop ?? shops.first);
|
|
||||||
} else {
|
} else {
|
||||||
configuration.shoppingService.shopService
|
widget.configuration.shoppingService.shopService.selectShop(
|
||||||
.selectShop(shops.first);
|
shops.first,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
return ListenableBuilder(
|
widget.configuration.shoppingService.shopService.selectShop(
|
||||||
listenable: configuration.shoppingService.shopService,
|
shops.first,
|
||||||
builder: (BuildContext context, Widget? _) {
|
|
||||||
configuration.onShopSelectionChange?.call(
|
|
||||||
configuration.shoppingService.shopService.selectedShop!,
|
|
||||||
);
|
);
|
||||||
return Stack(
|
}
|
||||||
children: [
|
return _ProductPageContent(
|
||||||
SingleChildScrollView(
|
configuration: widget.configuration,
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
configuration.shopselectorBuilder?.call(
|
|
||||||
context,
|
|
||||||
configuration,
|
|
||||||
shops,
|
|
||||||
configuration
|
|
||||||
.shoppingService.shopService.selectShop,
|
|
||||||
) ??
|
|
||||||
ShopSelector(
|
|
||||||
configuration: configuration,
|
|
||||||
shops: shops,
|
shops: shops,
|
||||||
onTap: configuration
|
|
||||||
.shoppingService.shopService.selectShop,
|
|
||||||
),
|
|
||||||
configuration.selectedCategoryBuilder?.call(
|
|
||||||
configuration,
|
|
||||||
) ??
|
|
||||||
SelectedCategories(
|
|
||||||
configuration: configuration,
|
|
||||||
),
|
|
||||||
_ShopContents(
|
|
||||||
configuration: configuration,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.bottomCenter,
|
|
||||||
child: configuration.shoppingCartButtonBuilder != null
|
|
||||||
? configuration.shoppingCartButtonBuilder!(
|
|
||||||
context,
|
|
||||||
configuration,
|
|
||||||
)
|
|
||||||
: DefaultShoppingCartButton(
|
|
||||||
configuration: configuration,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -157,33 +102,130 @@ class ProductPageScreen extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ShopContents extends StatelessWidget {
|
class _ProductPageContent extends StatefulWidget {
|
||||||
|
const _ProductPageContent({
|
||||||
|
required this.configuration,
|
||||||
|
required this.shops,
|
||||||
|
});
|
||||||
|
|
||||||
|
final ProductPageConfiguration configuration;
|
||||||
|
|
||||||
|
final List<Shop> shops;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_ProductPageContent> createState() => _ProductPageContentState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ProductPageContentState extends State<_ProductPageContent> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => Stack(
|
||||||
|
children: [
|
||||||
|
SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// shop selector
|
||||||
|
widget.configuration.shopselectorBuilder?.call(
|
||||||
|
context,
|
||||||
|
widget.configuration,
|
||||||
|
widget.shops,
|
||||||
|
widget
|
||||||
|
.configuration.shoppingService.shopService.selectShop,
|
||||||
|
) ??
|
||||||
|
ShopSelector(
|
||||||
|
configuration: widget.configuration,
|
||||||
|
shops: widget.shops,
|
||||||
|
onTap: (shop) {
|
||||||
|
widget.configuration.shoppingService.shopService
|
||||||
|
.selectShop(shop);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// selected categories
|
||||||
|
widget.configuration.selectedCategoryBuilder?.call(
|
||||||
|
widget.configuration,
|
||||||
|
) ??
|
||||||
|
SelectedCategories(
|
||||||
|
configuration: widget.configuration,
|
||||||
|
),
|
||||||
|
// products
|
||||||
|
_ShopContents(
|
||||||
|
configuration: widget.configuration,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// button
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: widget.configuration.shoppingCartButtonBuilder != null
|
||||||
|
? widget.configuration.shoppingCartButtonBuilder!(
|
||||||
|
context,
|
||||||
|
widget.configuration,
|
||||||
|
)
|
||||||
|
: DefaultShoppingCartButton(
|
||||||
|
configuration: widget.configuration,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ShopContents extends StatefulWidget {
|
||||||
const _ShopContents({
|
const _ShopContents({
|
||||||
required this.configuration,
|
required this.configuration,
|
||||||
});
|
});
|
||||||
|
|
||||||
final ProductPageConfiguration configuration;
|
final ProductPageConfiguration configuration;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_ShopContents> createState() => _ShopContentsState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ShopContentsState extends State<_ShopContents> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
widget.configuration.shoppingService.shopService.addListener(_listen);
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
widget.configuration.shoppingService.shopService.removeListener(_listen);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _listen() {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var theme = Theme.of(context);
|
var theme = Theme.of(context);
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.symmetric(
|
padding: EdgeInsets.symmetric(
|
||||||
horizontal: configuration.pagePadding.horizontal,
|
horizontal: widget.configuration.pagePadding.horizontal,
|
||||||
),
|
),
|
||||||
child: FutureBuilder(
|
child: FutureBuilder(
|
||||||
// ignore: discarded_futures
|
// ignore: discarded_futures
|
||||||
future: configuration.getProducts(
|
future: widget.configuration.getProducts(
|
||||||
configuration.shoppingService.shopService.selectedShop!,
|
widget.configuration.shoppingService.shopService.selectedShop!,
|
||||||
),
|
),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
|
List<Product> productPageContent;
|
||||||
|
|
||||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
return const Center(child: CircularProgressIndicator.adaptive());
|
return SizedBox(
|
||||||
|
height: MediaQuery.of(context).size.height * 0.7,
|
||||||
|
child: const Center(
|
||||||
|
child: CircularProgressIndicator.adaptive(),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (snapshot.hasError) {
|
if (snapshot.hasError) {
|
||||||
if (configuration.errorBuilder != null) {
|
if (widget.configuration.errorBuilder != null) {
|
||||||
return configuration.errorBuilder!(
|
return widget.configuration.errorBuilder!(
|
||||||
context,
|
context,
|
||||||
snapshot.error,
|
snapshot.error,
|
||||||
);
|
);
|
||||||
|
@ -192,13 +234,11 @@ class _ShopContents extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Product> productPageContent;
|
|
||||||
|
|
||||||
productPageContent =
|
productPageContent =
|
||||||
configuration.shoppingService.productService.products;
|
widget.configuration.shoppingService.productService.products;
|
||||||
|
|
||||||
if (productPageContent.isEmpty) {
|
if (productPageContent.isEmpty) {
|
||||||
return configuration.noContentBuilder?.call(context) ??
|
return widget.configuration.noContentBuilder?.call(context) ??
|
||||||
const DefaultNoContent();
|
const DefaultNoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,15 +250,15 @@ class _ShopContents extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
// Discounted product
|
// Discounted product
|
||||||
if (discountedproducts.isNotEmpty) ...[
|
if (discountedproducts.isNotEmpty) ...[
|
||||||
configuration.discountBuilder?.call(
|
widget.configuration.discountBuilder?.call(
|
||||||
context,
|
context,
|
||||||
configuration,
|
widget.configuration,
|
||||||
discountedproducts,
|
discountedproducts,
|
||||||
) ??
|
) ??
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: WeeklyDiscount(
|
child: WeeklyDiscount(
|
||||||
configuration: configuration,
|
configuration: widget.configuration,
|
||||||
product: discountedproducts.first,
|
product: discountedproducts.first,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -227,15 +267,15 @@ class _ShopContents extends StatelessWidget {
|
||||||
padding:
|
padding:
|
||||||
const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
|
const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
|
||||||
child: Text(
|
child: Text(
|
||||||
configuration.translations.categoryItemListTitle,
|
widget.configuration.translations.categoryItemListTitle,
|
||||||
style: theme.textTheme.titleLarge,
|
style: theme.textTheme.titleLarge,
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
configuration.categoryListBuilder?.call(
|
widget.configuration.categoryListBuilder?.call(
|
||||||
context,
|
context,
|
||||||
configuration,
|
widget.configuration,
|
||||||
productPageContent,
|
productPageContent,
|
||||||
) ??
|
) ??
|
||||||
Padding(
|
Padding(
|
||||||
|
@ -244,15 +284,11 @@ class _ShopContents extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
// Products
|
// Products
|
||||||
|
|
||||||
ListenableBuilder(
|
getCategoryList(
|
||||||
listenable:
|
|
||||||
configuration.shoppingService.productService,
|
|
||||||
builder: (context, _) => getCategoryList(
|
|
||||||
context,
|
context,
|
||||||
configuration,
|
widget.configuration,
|
||||||
configuration
|
widget.configuration.shoppingService.productService
|
||||||
.shoppingService.productService.products,
|
.products,
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
|
||||||
// Bottom padding so the last product is not cut off
|
// Bottom padding so the last product is not cut off
|
||||||
|
@ -268,55 +304,3 @@ class _ShopContents extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Selected categories.
|
|
||||||
class SelectedCategories extends StatelessWidget {
|
|
||||||
/// Constructor for the selected categories.
|
|
||||||
const SelectedCategories({
|
|
||||||
required this.configuration,
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Configuration for the product page.
|
|
||||||
final ProductPageConfiguration configuration;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
var theme = Theme.of(context);
|
|
||||||
return ListenableBuilder(
|
|
||||||
listenable: configuration.shoppingService.productService,
|
|
||||||
builder: (context, _) => Padding(
|
|
||||||
padding: const EdgeInsets.only(left: 4),
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
for (var category in configuration
|
|
||||||
.shoppingService.productService.selectedCategories) ...[
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(right: 8),
|
|
||||||
child: Chip(
|
|
||||||
backgroundColor: theme.colorScheme.primary,
|
|
||||||
deleteIcon: const Icon(
|
|
||||||
Icons.close,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
onDeleted: () {
|
|
||||||
configuration.shoppingService.productService
|
|
||||||
.selectCategory(category);
|
|
||||||
},
|
|
||||||
label: Text(
|
|
||||||
category,
|
|
||||||
style: theme.textTheme.bodyMedium
|
|
||||||
?.copyWith(color: Colors.white),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import "package:flutter/material.dart";
|
||||||
import "package:flutter_product_page/flutter_product_page.dart";
|
import "package:flutter_product_page/flutter_product_page.dart";
|
||||||
|
|
||||||
/// Default shopping cart button for the product page.
|
/// Default shopping cart button for the product page.
|
||||||
class DefaultShoppingCartButton extends StatelessWidget {
|
class DefaultShoppingCartButton extends StatefulWidget {
|
||||||
/// Constructor for the default shopping cart button for the product page.
|
/// Constructor for the default shopping cart button for the product page.
|
||||||
const DefaultShoppingCartButton({
|
const DefaultShoppingCartButton({
|
||||||
required this.configuration,
|
required this.configuration,
|
||||||
|
@ -12,20 +12,42 @@ class DefaultShoppingCartButton extends StatelessWidget {
|
||||||
/// Configuration for the product page.
|
/// Configuration for the product page.
|
||||||
final ProductPageConfiguration configuration;
|
final ProductPageConfiguration configuration;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<DefaultShoppingCartButton> createState() =>
|
||||||
|
_DefaultShoppingCartButtonState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DefaultShoppingCartButtonState extends State<DefaultShoppingCartButton> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
widget.configuration.shoppingService.shoppingCartService
|
||||||
|
.addListener(_listen);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
widget.configuration.shoppingService.shoppingCartService
|
||||||
|
.removeListener(_listen);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _listen() {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var theme = Theme.of(context);
|
var theme = Theme.of(context);
|
||||||
|
|
||||||
return ListenableBuilder(
|
return Padding(
|
||||||
listenable: configuration.shoppingService.shoppingCartService,
|
|
||||||
builder: (context, widget) => Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 60),
|
padding: const EdgeInsets.symmetric(horizontal: 60),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: FilledButton(
|
child: FilledButton(
|
||||||
onPressed: configuration
|
onPressed: widget.configuration.shoppingService.shoppingCartService
|
||||||
.shoppingService.shoppingCartService.products.isNotEmpty
|
.products.isNotEmpty
|
||||||
? configuration.onNavigateToShoppingCart
|
? widget.configuration.onNavigateToShoppingCart
|
||||||
: null,
|
: null,
|
||||||
style: theme.filledButtonTheme.style?.copyWith(
|
style: theme.filledButtonTheme.style?.copyWith(
|
||||||
backgroundColor: WidgetStateProperty.all(
|
backgroundColor: WidgetStateProperty.all(
|
||||||
|
@ -38,13 +60,12 @@ class DefaultShoppingCartButton extends StatelessWidget {
|
||||||
vertical: 12,
|
vertical: 12,
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
configuration.translations.navigateToShoppingCart,
|
widget.configuration.translations.navigateToShoppingCart,
|
||||||
style: theme.textTheme.displayLarge,
|
style: theme.textTheme.displayLarge,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_product_page/flutter_product_page.dart";
|
||||||
|
|
||||||
|
/// Selected categories.
|
||||||
|
class SelectedCategories extends StatefulWidget {
|
||||||
|
/// Constructor for the selected categories.
|
||||||
|
const SelectedCategories({
|
||||||
|
required this.configuration,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Configuration for the product page.
|
||||||
|
final ProductPageConfiguration configuration;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SelectedCategories> createState() => _SelectedCategoriesState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SelectedCategoriesState extends State<SelectedCategories> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
widget.configuration.shoppingService.productService.addListener(_listen);
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
widget.configuration.shoppingService.productService.removeListener(_listen);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _listen() {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var theme = Theme.of(context);
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 4),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
for (var category in widget.configuration.shoppingService
|
||||||
|
.productService.selectedCategories) ...[
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 8),
|
||||||
|
child: Chip(
|
||||||
|
backgroundColor: theme.colorScheme.primary,
|
||||||
|
deleteIcon: const Icon(
|
||||||
|
Icons.close,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
onDeleted: () {
|
||||||
|
widget.configuration.shoppingService.productService
|
||||||
|
.selectCategory(category);
|
||||||
|
},
|
||||||
|
label: Text(
|
||||||
|
category,
|
||||||
|
style: theme.textTheme.bodyMedium
|
||||||
|
?.copyWith(color: Colors.white),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,7 +5,7 @@ import "package:flutter_product_page/src/widgets/spaced_wrap.dart";
|
||||||
import "package:flutter_shopping_interface/flutter_shopping_interface.dart";
|
import "package:flutter_shopping_interface/flutter_shopping_interface.dart";
|
||||||
|
|
||||||
/// Shop selector widget that displays a list to navigate between shops.
|
/// Shop selector widget that displays a list to navigate between shops.
|
||||||
class ShopSelector extends StatelessWidget {
|
class ShopSelector extends StatefulWidget {
|
||||||
/// Constructor for the shop selector.
|
/// Constructor for the shop selector.
|
||||||
const ShopSelector({
|
const ShopSelector({
|
||||||
required this.configuration,
|
required this.configuration,
|
||||||
|
@ -33,30 +33,53 @@ class ShopSelector extends StatelessWidget {
|
||||||
/// Padding on the buttons.
|
/// Padding on the buttons.
|
||||||
final double paddingOnButtons;
|
final double paddingOnButtons;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ShopSelector> createState() => _ShopSelectorState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ShopSelectorState extends State<ShopSelector> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
widget.configuration.shoppingService.shopService.addListener(_listen);
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
widget.configuration.shoppingService.shopService.removeListener(_listen);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _listen() {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (shops.length == 1) {
|
if (widget.shops.length == 1) {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (configuration.shopSelectorStyle == ShopSelectorStyle.spacedWrap) {
|
if (widget.configuration.shopSelectorStyle ==
|
||||||
|
ShopSelectorStyle.spacedWrap) {
|
||||||
return SpacedWrap(
|
return SpacedWrap(
|
||||||
shops: shops,
|
shops: widget.shops,
|
||||||
selectedItem:
|
selectedItem:
|
||||||
configuration.shoppingService.shopService.selectedShop!.id,
|
widget.configuration.shoppingService.shopService.selectedShop!.id,
|
||||||
onTap: onTap,
|
onTap: widget.onTap,
|
||||||
width: MediaQuery.of(context).size.width - (16 * 2),
|
width: MediaQuery.of(context).size.width - (16 * 2),
|
||||||
paddingBetweenButtons: paddingBetweenButtons,
|
paddingBetweenButtons: widget.paddingBetweenButtons,
|
||||||
paddingOnButtons: paddingOnButtons,
|
paddingOnButtons: widget.paddingOnButtons,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return HorizontalListItems(
|
return HorizontalListItems(
|
||||||
shops: shops,
|
shops: widget.shops,
|
||||||
selectedItem: configuration.shoppingService.shopService.selectedShop!.id,
|
selectedItem:
|
||||||
onTap: onTap,
|
widget.configuration.shoppingService.shopService.selectedShop!.id,
|
||||||
paddingBetweenButtons: paddingBetweenButtons,
|
onTap: widget.onTap,
|
||||||
paddingOnButtons: paddingOnButtons,
|
paddingBetweenButtons: widget.paddingBetweenButtons,
|
||||||
|
paddingOnButtons: widget.paddingOnButtons,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@ class ShoppingConfiguration {
|
||||||
this.onNavigateToShoppingCart,
|
this.onNavigateToShoppingCart,
|
||||||
this.getProductsInShoppingCart,
|
this.getProductsInShoppingCart,
|
||||||
this.shoppingCartButtonBuilder,
|
this.shoppingCartButtonBuilder,
|
||||||
this.initialShopid,
|
|
||||||
this.productBuilder,
|
this.productBuilder,
|
||||||
this.onShopSelectionChange,
|
this.onShopSelectionChange,
|
||||||
this.productPageTranslations,
|
this.productPageTranslations,
|
||||||
|
@ -82,9 +81,6 @@ class ShoppingConfiguration {
|
||||||
final Widget Function(BuildContext, ProductPageConfiguration)?
|
final Widget Function(BuildContext, ProductPageConfiguration)?
|
||||||
shoppingCartButtonBuilder;
|
shoppingCartButtonBuilder;
|
||||||
|
|
||||||
/// Initial shop that will be selected
|
|
||||||
final String? initialShopid;
|
|
||||||
|
|
||||||
/// ProductPage item builder
|
/// ProductPage item builder
|
||||||
final Widget Function(
|
final Widget Function(
|
||||||
BuildContext,
|
BuildContext,
|
||||||
|
|
|
@ -37,10 +37,8 @@ class ShoppingProductPage extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var service = shoppingConfiguration.shoppingService;
|
var service = shoppingConfiguration.shoppingService;
|
||||||
return ProductPageScreen(
|
return ProductPageScreen(
|
||||||
initialBuildShopId: shoppingConfiguration.initialShopid,
|
|
||||||
configuration: ProductPageConfiguration(
|
configuration: ProductPageConfiguration(
|
||||||
shoppingService: service,
|
shoppingService: service,
|
||||||
initialShopId: shoppingConfiguration.initialShopid,
|
|
||||||
shoppingCartButtonBuilder:
|
shoppingCartButtonBuilder:
|
||||||
shoppingConfiguration.shoppingCartButtonBuilder,
|
shoppingConfiguration.shoppingCartButtonBuilder,
|
||||||
productBuilder: shoppingConfiguration.productBuilder,
|
productBuilder: shoppingConfiguration.productBuilder,
|
||||||
|
|
|
@ -62,7 +62,17 @@ class LocalProductService with ChangeNotifier implements ProductService {
|
||||||
description: "This is a delicious Brown fish",
|
description: "This is a delicious Brown fish",
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// only return items that match the selectedcategories
|
||||||
_allProducts = List.from(_products);
|
_allProducts = List.from(_products);
|
||||||
|
|
||||||
|
_products = _products.where((element) {
|
||||||
|
if (_selectedCategories.isEmpty) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return _selectedCategories.contains(element.category);
|
||||||
|
}).toList();
|
||||||
|
|
||||||
return Future.value(_products);
|
return Future.value(_products);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue