mirror of
https://github.com/Iconica-Development/flutter_shopping.git
synced 2025-05-19 08:53:46 +02:00
feat: add filter screen and widgets
This commit is contained in:
parent
b736072497
commit
db1299f22b
8 changed files with 209 additions and 23 deletions
|
@ -0,0 +1,67 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:flutter_product_page/flutter_product_page.dart";
|
||||
|
||||
/// Category selection screen.
|
||||
class CategorySelectionScreen extends StatelessWidget {
|
||||
/// Constructor for the category selection screen.
|
||||
const CategorySelectionScreen({
|
||||
required this.configuration,
|
||||
super.key,
|
||||
});
|
||||
|
||||
/// Configuration for the product page.
|
||||
final ProductPageConfiguration configuration;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var theme = Theme.of(context);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: const SizedBox.shrink(),
|
||||
title: Text(
|
||||
"filter",
|
||||
style: theme.textTheme.headlineLarge,
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
icon: const Icon(Icons.close),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: ListenableBuilder(
|
||||
listenable: configuration.shoppingService.productService,
|
||||
builder: (context, _) => Column(
|
||||
children: [
|
||||
...configuration.shoppingService.productService.getCategories().map(
|
||||
(category) {
|
||||
var isChecked = configuration
|
||||
.shoppingService.productService.selectedCategories
|
||||
.contains(category);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: CheckboxListTile(
|
||||
activeColor: theme.colorScheme.primary,
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
value: isChecked,
|
||||
onChanged: (value) {
|
||||
configuration.shoppingService.productService
|
||||
.selectCategory(category);
|
||||
},
|
||||
shape: const UnderlineInputBorder(),
|
||||
title: Text(
|
||||
category,
|
||||
style: theme.textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -29,6 +29,7 @@ class ProductPageConfiguration {
|
|||
this.shopselectorBuilder,
|
||||
this.discountBuilder,
|
||||
this.categoryListBuilder,
|
||||
this.selectedCategoryBuilder,
|
||||
}) {
|
||||
onProductDetail ??= _onProductDetail;
|
||||
discountDescription ??= _defaultDiscountDescription;
|
||||
|
@ -143,6 +144,10 @@ class ProductPageConfiguration {
|
|||
ProductPageConfiguration configuration,
|
||||
List<Product> products,
|
||||
)? categoryListBuilder;
|
||||
|
||||
/// Builder for the list of selected categories
|
||||
final Widget Function(ProductPageConfiguration configuration)?
|
||||
selectedCategoryBuilder;
|
||||
}
|
||||
|
||||
Future<void> _onProductDetail(
|
||||
|
|
|
@ -142,6 +142,12 @@ class _ProductPage extends StatelessWidget {
|
|||
shops: shops,
|
||||
onTap: configuration.shoppingService.shopService.selectShop,
|
||||
),
|
||||
configuration.selectedCategoryBuilder?.call(
|
||||
configuration,
|
||||
) ??
|
||||
SelectedCategories(
|
||||
configuration: configuration,
|
||||
),
|
||||
_ShopContents(
|
||||
configuration: configuration,
|
||||
),
|
||||
|
@ -203,31 +209,16 @@ class _ShopContents extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
var productPageContent = snapshot.data;
|
||||
List<Product> productPageContent;
|
||||
|
||||
if (productPageContent == null || productPageContent.isEmpty) {
|
||||
productPageContent =
|
||||
configuration.shoppingService.productService.products;
|
||||
|
||||
if (productPageContent.isEmpty) {
|
||||
return configuration.noContentBuilder?.call(context) ??
|
||||
const DefaultNoContent();
|
||||
}
|
||||
|
||||
var productList = Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 12, 16, 8),
|
||||
child: Column(
|
||||
children: [
|
||||
// Products
|
||||
|
||||
getCategoryList(
|
||||
context,
|
||||
configuration,
|
||||
productPageContent,
|
||||
),
|
||||
|
||||
// Bottom padding so the last product is not cut off
|
||||
// by the to shopping cart button.
|
||||
const SizedBox(height: 48),
|
||||
],
|
||||
),
|
||||
);
|
||||
var discountedproducts = productPageContent
|
||||
.where((product) => product.hasDiscount)
|
||||
.toList();
|
||||
|
@ -264,7 +255,29 @@ class _ShopContents extends StatelessWidget {
|
|||
configuration,
|
||||
productPageContent,
|
||||
) ??
|
||||
productList,
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 12, 16, 8),
|
||||
child: Column(
|
||||
children: [
|
||||
// Products
|
||||
|
||||
ListenableBuilder(
|
||||
listenable:
|
||||
configuration.shoppingService.productService,
|
||||
builder: (context, _) => getCategoryList(
|
||||
context,
|
||||
configuration,
|
||||
configuration
|
||||
.shoppingService.productService.products,
|
||||
),
|
||||
),
|
||||
|
||||
// Bottom padding so the last product is not cut off
|
||||
// by the to shopping cart button.
|
||||
const SizedBox(height: 48),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
|
@ -272,3 +285,55 @@ 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),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:flutter_product_page/flutter_product_page.dart";
|
||||
import "package:flutter_product_page/src/category_selection_screen.dart";
|
||||
|
||||
/// Default appbar for the product page.
|
||||
class DefaultAppbar extends StatelessWidget implements PreferredSizeWidget {
|
||||
|
@ -19,7 +20,18 @@ class DefaultAppbar extends StatelessWidget implements PreferredSizeWidget {
|
|||
return AppBar(
|
||||
leading: IconButton(onPressed: () {}, icon: const Icon(Icons.person)),
|
||||
actions: [
|
||||
IconButton(onPressed: () {}, icon: const Icon(Icons.filter_alt)),
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => CategorySelectionScreen(
|
||||
configuration: configuration,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.filter_alt),
|
||||
),
|
||||
],
|
||||
title: Text(
|
||||
configuration.translations.appBarTitle,
|
||||
|
|
|
@ -29,6 +29,7 @@ class ShoppingConfiguration {
|
|||
this.categoryListBuilder,
|
||||
this.shopselectorBuilder,
|
||||
this.discountBuilder,
|
||||
this.selectedCategoryBuilder,
|
||||
|
||||
/// ShoppingCart configurations
|
||||
this.onConfirmOrder,
|
||||
|
@ -57,6 +58,10 @@ class ShoppingConfiguration {
|
|||
/// The service that will be used for the userstory
|
||||
final ShoppingService shoppingService;
|
||||
|
||||
/// Builder for the list of selected categories
|
||||
final Widget Function(ProductPageConfiguration configuration)?
|
||||
selectedCategoryBuilder;
|
||||
|
||||
/// Function that will be called when the products are requested
|
||||
final Future<List<Product>> Function(String shopId)? onGetProducts;
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ class ShoppingProductPage extends StatelessWidget {
|
|||
shopselectorBuilder: shoppingConfiguration.shopselectorBuilder,
|
||||
discountBuilder: shoppingConfiguration.discountBuilder,
|
||||
categoryListBuilder: shoppingConfiguration.categoryListBuilder,
|
||||
selectedCategoryBuilder: shoppingConfiguration.selectedCategoryBuilder,
|
||||
shops: () async {
|
||||
if (shoppingConfiguration.onGetShops != null) {
|
||||
return shoppingConfiguration.onGetShops!();
|
||||
|
|
|
@ -14,4 +14,10 @@ abstract class ProductService with ChangeNotifier {
|
|||
|
||||
/// Get current Products
|
||||
List<Product> get products;
|
||||
|
||||
/// Get current Products
|
||||
List<String> get selectedCategories;
|
||||
|
||||
/// Select a category
|
||||
void selectCategory(String category);
|
||||
}
|
||||
|
|
|
@ -4,10 +4,12 @@ import "package:flutter_shopping_interface/flutter_shopping_interface.dart";
|
|||
/// Local product service
|
||||
class LocalProductService with ChangeNotifier implements ProductService {
|
||||
List<Product> _products = [];
|
||||
List<Product> _allProducts = [];
|
||||
final List<String> _selectedCategories = [];
|
||||
|
||||
@override
|
||||
List<String> getCategories() =>
|
||||
_products.map((e) => e.category).toSet().toList();
|
||||
_allProducts.map((e) => e.category).toSet().toList();
|
||||
|
||||
@override
|
||||
Future<Product> getProduct(String id) =>
|
||||
|
@ -60,9 +62,32 @@ class LocalProductService with ChangeNotifier implements ProductService {
|
|||
description: "This is a delicious Brown fish",
|
||||
),
|
||||
];
|
||||
_allProducts = List.from(_products);
|
||||
return Future.value(_products);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Product> get products => _products;
|
||||
|
||||
@override
|
||||
void selectCategory(String category) {
|
||||
if (_selectedCategories.contains(category)) {
|
||||
_selectedCategories.remove(category);
|
||||
} else {
|
||||
_selectedCategories.add(category);
|
||||
}
|
||||
if (_selectedCategories.isEmpty) {
|
||||
_products = List.from(_allProducts);
|
||||
}
|
||||
_products = _allProducts.where((element) {
|
||||
if (_selectedCategories.isEmpty) {
|
||||
return true;
|
||||
}
|
||||
return _selectedCategories.contains(element.category);
|
||||
}).toList();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@override
|
||||
List<String> get selectedCategories => _selectedCategories;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue