I'm trying to write a shopping app. But unfortunately I stuck with loading and displaying the products from firebase with the help of the package provider. In the app you can get from the searchPage to the ResultPage. In the ResultPage a GridView.builder should display the products whose category matches your entered searchtext from the SearchPage. From the ResultPage you can also get to a FilterScreen where you can set filters. After you have left this screen you should only see the products on the result screen which matches your filters. But this isn't working at all. Instead of seeing the filtered products there are no products displayed. But I get no error message. So I really stuck at this problem. I don't even know where it's coming from. I would be very thankful if anyone could help me!
This is my result page code:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shs/providers/filters_provider.dart';
import 'package:shs/providers/products_provider.dart';
import 'package:shs/screens/filters_screen.dart';
import 'package:shs/widgets/bottom_navigation_bar.dart';
import 'package:shs/widgets/filter_overlay.dart';
import 'package:shs/widgets/my_scaffold.dart';
import 'package:shs/widgets/product_grid.dart';
import '../widgets/size_grid.dart';
import '../widgets/size_grid_buchstaben.dart';
class ResultPage extends StatefulWidget {
String searchtext;
String? gender;
ResultPage(
this.searchtext,
this.gender,
);
@override
State<ResultPage> createState() => _ResultPageState();
}
class _ResultPageState extends State<ResultPage> {
final searchController = TextEditingController();
@override
void initState() {
searchController.addListener(() => setState(() {}));
super.initState();
}
@override
Widget build(BuildContext context) {
return MyScaffold(
body: SingleChildScrollView(
child: Column(
children: [
SizedBox(
height: 30,
),
Container(
height: 47,
margin: EdgeInsets.only(top: 30, left: 30, right: 30),
child: TextField(
onSubmitted: ((value) {
setState(() {
widget.searchtext = value;
});
}),
controller: searchController,
textInputAction: TextInputAction.search,
style: Theme.of(context).textTheme.bodyMedium,
decoration: InputDecoration(
fillColor: Colors.white,
suffixIcon: searchController.text.isEmpty
? Container(
width: 0,
)
: IconButton(
onPressed: () {
searchController.clear();
},
icon: Icon(
Icons.close_rounded,
color: Colors.black,
)),
border:
OutlineInputBorder(borderRadius: BorderRadius.circular(15)),
prefixIcon: Container(
padding: const EdgeInsets.only(top: 5, left: 8),
child: Image.asset(
'lib/asset/icons/Search.png',
),
),
hintText: 'Search',
hintStyle: Theme.of(context).textTheme.bodyMedium,
),
),
),
SizedBox(
height: 30,
),
Padding(
padding: const EdgeInsets.only(right: 230),
child: Container(
width: 109,
height: 40,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
color: Theme.of(context).colorScheme.primary,
),
child: GestureDetector(
onTap: () {
Navigator.of(context).pushNamed(FiltersScreen.routeName);
},
child: Row(
children: [
SizedBox(
width: 10,
),
Icon(Icons.filter_list_alt),
SizedBox(
width: 10,
),
Text(
'Filter',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(fontSize: 15),
)
],
),
)),
),
SizedBox(
height: 30,
),
Consumer<FilterProvider>(
builder: (context, filters, _) => ProductGrid(
filterColor: filters.filterMap['color']!,
filterZustand: filters.filterMap['state']!,
filtersize: filters.filterMap['size']!,
filterProdCat: widget.searchtext,
filterprice: filters.filteredPrice,
)), //Marke noch überall hnzufügen, wenn Filter eingefügt + searchtext zur filters.filterList hinzufügen und wie die anderen darauf referenzieeren, Suchfeld für alle Attribute verwendbar machen
SizedBox(
height: 30,
),
MyBottomNavigationBar(),
],
)),
);
}
}
This is my FilterProviderCode
import 'package:flutter/material.dart';
class FilterProvider with ChangeNotifier {
Map<String, List<String>> _filterMap = {'size': [], 'state': [], 'color': []};
double? filteredPrice;
var _expandedSize = false;
var _expandedPrice = false;
var _expandedState = false;
var _expandedColor = false;
var _expandedBrand = false;
bool _selected = false;
bool get selected {
return _selected;
}
bool _selectedSize = false;
bool _selectedState1 = false;
bool _selectedState2 = false;
bool _selectedState3 = false;
bool _selectedState4 = false;
bool _selectedState5 = false;
bool _selectedColor1 = false;
bool _selectedColor2 = false;
bool _selectedColor3 = false;
bool _selectedColor4 = false;
bool _selectedColor5 = false;
bool _selectedColor6 = false;
bool _selectedColor7 = false;
bool _selectedColor8 = false;
bool get selectedSize {
return _selectedSize;
}
Map<String, List<String>> get filterMap {
return _filterMap;
}
void setSelectedSize(bool variable) {
_selectedSize = variable;
}
void setSelectedState1(bool variable) {
_selectedState1 = variable;
}
void setSelectedState2(bool variable) {
_selectedState2 = variable;
}
void setSelectedState3(bool variable) {
_selectedState3 = variable;
}
void setSelectedState4(bool variable) {
_selectedState4 = variable;
}
void setSelectedState5(bool variable) {
_selectedState5 = variable;
}
void setSelectedColor1(bool variable) {
_selectedColor1 = variable;
}
void setSelectedColor2(bool variable) {
_selectedColor2 = variable;
}
void setSelectedColor3(bool variable) {
_selectedColor3 = variable;
}
void setSelectedColor4(bool variable) {
_selectedColor4 = variable;
}
void setSelectedColor5(bool variable) {
_selectedColor5 = variable;
}
void setSelectedColor6(bool variable) {
_selectedColor6 = variable;
}
void setSelectedColor7(bool variable) {
_selectedColor7 = variable;
}
void setSelectedColor8(bool variable) {
_selectedColor8 = variable;
}
bool get selectedState1 {
return _selectedState1;
}
bool get selectedState2 {
return _selectedState2;
}
bool get selectedState3 {
return _selectedState3;
}
bool get selectedState4 {
return _selectedState4;
}
bool get selectedState5 {
return _selectedState5;
}
bool get selectedColor1 {
return _selectedColor1;
}
bool get selectedColor2 {
return _selectedColor2;
}
bool get selectedColor3 {
return _selectedColor3;
}
bool get selectedColor4 {
return _selectedColor4;
}
bool get selectedColor5 {
return _selectedColor5;
}
bool get selectedColor6 {
return _selectedColor6;
}
bool get selectedColor7 {
return _selectedColor7;
}
bool get selectedColor8 {
return _selectedColor8;
}
bool get expandedSize {
return _expandedSize;
}
bool get expandedPrice {
return _expandedPrice;
}
bool get expandedState {
return _expandedState;
}
bool get expandedColor {
return _expandedColor;
}
bool get expandedBrand {
return _expandedBrand;
}
void expandSize() {
_expandedSize = !_expandedSize;
notifyListeners();
}
void expandPrice() {
_expandedPrice = !_expandedPrice;
notifyListeners();
}
void expandState() {
_expandedState = !_expandedState;
notifyListeners();
}
void expandColor() {
_expandedColor = !_expandedColor;
notifyListeners();
}
void expandBrand() {
_expandedBrand = !_expandedBrand;
notifyListeners();
}
void applyFilter(String key, String entry) {
_selected = !_selected;
if (_selected) {
_filterMap[key]!.add(entry);
}
if (_selected == false) {
_filterMap[key]!.removeWhere((element) => element == entry);
}
print(_filterMap);
notifyListeners();
}
void applyFilterUpdate(double entry) {
filteredPrice = entry;
notifyListeners();
print(filteredPrice);
}
}
This is my ProductProvider Code
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:shs/models/product.dart';
class ProductProvider with ChangeNotifier {
final db = FirebaseFirestore.instance;
List<Product> _items = [];
List<Product> get items {
return [..._items];
}
/*Stream<QuerySnapshot> get stream {
return db
.collection('products')
.withConverter(
fromFirestore: Product.fromFirestore,
toFirestore: (Product product, _) => product.toFirestore(),
)
.snapshots();
}*/
Future<void> fetchProducts(
{required bool showAll,
List<dynamic>?
filtersize, //List ist halt manchmal leer wie bei allen anderen
double? filterprice,
List<dynamic>? filterZustand,
//required List<String>? filterMarke,
List<dynamic>? filterColor,
String? filterProdCat}) async {
List<Product> filteredProducts = [];
List<Product> allProducts = [];
List<String> allIDs = [];
final ref = db.collection('products').withConverter(
fromFirestore: Product.fromFirestore,
toFirestore: (Product product, _) => product.toFirestore(),
);
ref.snapshots().listen((event) {
List<String> products = [];
for (var doc in event.docs) {
products.add(doc.id);
allIDs = products;
}
});
allIDs.forEach((id) async {
final docSnap = await ref.doc(id).get();
final product = docSnap.data();
if (product != null) {
allProducts.add(product);
} else {
print('No such document.');
}
});
filteredProducts = allProducts.where((product) {
return filtersize!.contains(product
.size) || //Liste ist halt manchmal leer, also nur wahr wenn auch wirklich Filter gesetzt sind
product.price == filterprice ||
filterZustand!.contains(product.zustand) ||
//filterMarke.contains(product.marke) ||
filterColor!.contains(product.color) ||
product.prodCat == filterProdCat;
}).toList(); // nur wenn filtern erwünscht alle Filter Lists angeben, auch wenn sie leer sind sonst null
if (showAll) {
_items = allProducts;
} else {
_items = filteredProducts;
}
//notifyListeners();
}
}
This is my FiltersScreen Code
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shs/providers/filters_provider.dart';
import 'package:shs/widgets/filter_color.dart';
import 'package:shs/widgets/filter_state.dart';
import 'package:shs/widgets/marked_text.dart';
import 'package:shs/widgets/size_grid.dart';
import 'package:shs/widgets/size_grid_buchstaben.dart';
import 'package:shs/widgets/size_grid_hosen.dart';
import 'package:shs/widgets/size_grid_shoes.dart';
class FiltersScreen extends StatefulWidget {
static const routeName = '/filters';
@override
State<FiltersScreen> createState() => _FiltersScreenState();
}
class _FiltersScreenState extends State<FiltersScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
leading: IconButton(
icon: Icon(
Icons.close,
size: 50,
),
onPressed: () {
Navigator.of(context).pop();
},
),
backgroundColor: Theme.of(context).colorScheme.primary,
actions: [
GestureDetector(
onTap: () {}, // Navigate to AccountPage
child: Container(
child: Image.asset('lib/asset/icons/Account.png'),
),
),
SizedBox(
width: 7,
),
GestureDetector(
onTap: () {}, // Navigate to ShoppingBag
child: Container(
child: Image.asset('lib/asset/icons/ShoppingBag.png'),
),
),
GestureDetector(
onTap: () {}, // Navigate to Favorite
child: Container(
child: Image.asset('lib/asset/icons/Favorite.png'),
),
),
SizedBox(
width: 7,
),
],
),
body: Consumer<FilterProvider>(
builder: (context, filters, _) => SingleChildScrollView(
child: Column(children: [
SizedBox(
height: 30,
),
Center(
child: MarkedText(
'Filters',
() {},
Theme.of(context).colorScheme.primary,
fontsize: 80,
),
),
SizedBox(
height: 30,
),
ListTile(
title: Text(
'Size',
style: Theme.of(context)
.textTheme
.displayMedium!
.copyWith(fontSize: 50),
),
trailing: IconButton(
onPressed: () {
filters.expandSize();
},
icon: Icon(
filters.expandedSize
? Icons.expand_less
: Icons.expand_more,
size: 50,
color: Colors.black)),
),
if (filters.expandedSize)
Container(
height: 1800,
child: Column(
children: [
SizedBox(
height: 30,
),
SizedBox(
height: 300,
child: Container(
width: 300, height: 500, child: SizeGrid())),
SizedBox(
height: 30,
),
SizedBox(
height: 230,
child:
Container(width: 300, child: SizeGridBuchstaben())),
SizedBox(
height: 30,
),
SizedBox(
height: 450,
child: Container(width: 300, child: SizeGridHosen())),
SizedBox(
height: 30,
),
Text(
'Size Shoes',
style: Theme.of(context)
.textTheme
.displayMedium!
.copyWith(fontSize: 50),
),
SizedBox(
height: 30,
),
SizedBox(
height: 530,
child: Container(width: 300, child: SizeGridShoes())),
SizedBox(
height: 30,
),
],
),
),
ListTile(
title: Text(
'Price',
style: Theme.of(context)
.textTheme
.displayMedium!
.copyWith(fontSize: 50),
),
trailing: IconButton(
onPressed: () {
filters.expandPrice();
},
icon: Icon(
filters.expandedPrice
? Icons.expand_less
: Icons.expand_more,
size: 50,
color: Colors.black)),
),
if (filters.expandedPrice)
Container(
child: Row(children: [
SizedBox(
width: 20,
),
Text(
'bis',
style: Theme.of(context)
.textTheme
.titleMedium!
.copyWith(fontSize: 32),
),
SizedBox(
width: 30,
),
Container(
width: 150,
height: 40,
child: TextField(
keyboardType: TextInputType.number,
onSubmitted: ((value) {
final doubleValue = double.parse(value);
filters.applyFilterUpdate(doubleValue);
}),
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(15)),
hintText: '\$',
hintStyle: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(fontSize: 24)),
)),
]),
),
ListTile(
title: Text(
'State',
style: Theme.of(context)
.textTheme
.displayMedium!
.copyWith(fontSize: 50),
),
trailing: IconButton(
onPressed: () {
filters.expandState();
},
icon: Icon(
filters.expandedState
? Icons.expand_less
: Icons.expand_more,
size: 50,
color: Colors.black)),
),
if (filters.expandedState) FiltersState(),
ListTile(
title: Text(
'Color',
style: Theme.of(context)
.textTheme
.displayMedium!
.copyWith(fontSize: 50),
),
trailing: IconButton(
onPressed: () {
filters.expandColor();
},
icon: Icon(
filters.expandedColor
? Icons.expand_less
: Icons.expand_more,
size: 50,
color: Colors.black)),
),
if (filters.expandedColor) FilterColor(),
ListTile(
title: Text(
'Brand',
style: Theme.of(context)
.textTheme
.displayMedium!
.copyWith(fontSize: 50),
),
trailing: IconButton(
onPressed: () {
filters.expandBrand();
},
icon: Icon(
filters.expandedBrand
? Icons.expand_less
: Icons.expand_more,
size: 50,
color: Colors.black)),
), //Marken aufzählen List Tile von Zustand verwenden
SizedBox(
height: 30,
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop(); //Filter anwenden
},
child: Text('Apply Filters!')),
]),
),
),
);
}
}
This is my Product Grid Code:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shs/providers/products_provider.dart';
import 'product_card.dart';
class ProductGrid extends StatelessWidget {
ScrollController scrollController = ScrollController();
List<String> filtersize;
List<String> filterColor;
String? filterProdCat;
List<String> filterZustand;
double? filterprice;
ProductGrid(
{required this.filterColor,
this.filterProdCat,
required this.filterZustand,
this.filterprice,
required this.filtersize});
Future<void> updateProducts(
{required BuildContext context,
required List<String> filtersize,
required List<String> filterColor,
//required List<String> filterMarke,
String? filterProdCat,
required List<String> filterZustand,
double? filterprice}) async {
await Provider.of<ProductProvider>(
context,
listen: false,
).fetchProducts(
showAll: false,
filtersize: filtersize,
filterColor: filterColor,
//filterMarke: filterMarke,
filterProdCat: filterProdCat,
filterZustand: filterZustand,
filterprice: filterprice);
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: updateProducts(
context: context,
filtersize: filtersize,
filterColor: filterColor,
filterZustand: filterZustand),
builder: ((context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
return Consumer<ProductProvider>(
builder: (ctx, products, _) => GridView.builder(
controller: scrollController,
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: products.items.length,
itemBuilder: ((context, index) => ProductCard(
state: products.items[index].zustand!,
imageUrl: products.items[index].imageUrl,
price: products.items[index].price,
size: products.items[index].size!,
id: products.items[index].id,
prodCat: products.items[index].prodCat,
marke: products.items[index].marke)),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 10,
mainAxisExtent: 400)));
}));
}
}
And here is my dummy product data from firestore
If you need more code or anything else, no problem I will post it! I'm new to Flutter so my code is a bit messy, I have to admit...
THANK YOU FOR YOUR HELP!