I'm facing quite a strange problem when I try fetching data from the server. It seems like these responses are repeating themselves when I try displaying them as per their categories defined in the JSON response. To make things easier to understand, I have posted the JSON response, my code and screenshots along with the error.
The Restaurant product JSON response:
Endpoint: https://***********/current_work/*****/api/restaurant_product/{id}
The idea here is to fetch Restaurant Products as per the Restaurant ID(id)
{
"status": "success",
"data": [
{
"product_id": 17,
"category_name": [ //This here is the Category Name. We need to filter products based on this. Currently I'm only using the element at the first index
"Veg",
"Dinner"
],
"restaurant_name": "Mocambo",
"product_name": "Panner Makhani",
"product_description": "Mouth Smacking Creamy Indian Gravy.",
"product_image": "/assets/product/WOw4Rc03-02-08.jpg",
"product_selling_price": "240",
"product_status": "active",
"product_quantity": "50",
"product_rating": "",
"product_rating_count": "",
"product_sell_count": "0"
},
{
"product_id": 16,
"category_name": [
"Veg",
"Dinner"
],
"restaurant_name": "Mocambo",
"product_name": "Panner Makhani",
"product_description": "Mouth Smacking Creamy Indian Gravy.",
"product_image": "/assets/product/WOw4Rc03-02-08.jpg",
"product_selling_price": "240",
"product_status": "active",
"product_quantity": "50",
"product_rating": "",
"product_rating_count": "",
"product_sell_count": "0"
},
{
"product_id": 15,
"category_name": [
"NonVeg",
"Snacks"
],
"restaurant_name": "Mocambo",
"product_name": "Cheese Steak Burger",
"product_description": "Tasty steak burger oozing with the goodness of creamy cheese",
"product_image": "/assets/product/SBuZnx02-54-20.jpg",
"product_selling_price": "150",
"product_status": "active",
"product_quantity": "20",
"product_rating": "",
"product_rating_count": "",
"product_sell_count": "0"
}
]
}
This is how it loads at the start which is what it should be like.
However, if I go back to the previous screen and then access this screen again, I get a repetition of the products that are currently displayed and this keeps on repeating itself every time I do the same.
The error I get upon initially landing on this screen:
The following assertion was thrown while dispatching notifications for RestaurantProductProvider:
setState() or markNeedsBuild() called during build.
This _InheritedProviderScope<RestaurantProductProvider?> widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was: _InheritedProviderScope<RestaurantProductProvider?>
value: Instance of 'RestaurantProductProvider'
listening to value
The widget which was currently being built when the offending call was made was: NotificationListener<KeepAliveNotification>
When the exception was thrown, this was the stack
The code:
RestaurantProductProvider
Map<String, dynamic> _map = {};
List<dynamic> list = [];
final List<dynamic> _products = [];
List<dynamic> _productList = [];
final List<dynamic> _category = [];
Map<String, dynamic> get map {
return {..._map};
}
List<dynamic> get category {
return [..._category];
}
List<dynamic> get products {
return [..._products];
}
List<dynamic> get productList {
return [..._productList];
}
Future<List<dynamic>> fetchCategory(String id) async {
final url = Uri.parse(baseUrl 'api/restaurant_product/$id');
final response = await http.get(url);
Category category = categoryFromJson(response.body);
_map = category.toJson();
_map['data'].forEach((value) => list.add(value['category_name'][0]));
_map['data'].forEach((value) => _products.add(value));
// !_category.contains(list[0]) ? _category.add(list[0]) : null;
print('List $list');
print('Category From Response $_category');
list.forEach((value) {
!_category.contains(value) ? _category.add(value) : null;
});
return _category;
}
Future<void> productFilter(String category) async { //This is where category is passed
_productList = _products.where((element) {
return element['category_name'].contains(category);
}).toList();
print('Product List $_productList');
notifyListeners();
}
The filtering happens as per the category selected by the user. Check the response above and look for the category key.
For some strange reason, the error gets pointed at the end of the above code. The other error widget thrown in the stack is as follows:
class SideDishState extends State<SideDish> {
bool isLoading = true;
@override
void didChangeDependencies() {
// TODO: implement initState
print('This is the Category ${widget.category}');
Provider.of<RestaurantProductProvider>(context, listen: false)
.productFilter(widget.category) //This is where the error gets thrown
.then((_) {
setState(() {
isLoading = false;
});
});
super.didChangeDependencies();
}
@override
Widget build(BuildContext context) {
final width = MediaQuery.of(context).size.width;
final height = MediaQuery.of(context).size.height;
final textScale = MediaQuery.of(context).textScaleFactor * 1.2;
// int counter = 0;
final provider =
Provider.of<RestaurantProductProvider>(context).productList;
// TODO: implement build
return Scaffold(
body: isLoading
? const Center(
child: CircularProgressIndicator(
color: Colors.red,
),
)
: Container(
width: width * 1,
height: height * 1,
// color: Colors.red,
padding:
EdgeInsets.only(left: width * 0.05, right: width * 0.05),
// color: Colors.red,
child: ListView.builder(
itemBuilder: (context, index) => InkWell(
onTap: () => Navigator.pushNamed(context, '/item-details',
arguments: {
'id': provider[index]["product_id"],
'name': provider[index]["product_name"],
'image': provider[index]['product_image'],
'price': provider[index]["product_selling_price"],
'restaurantName': provider[index]['restaurant_name'],
// 'rating': widget.rating,
'rating': provider[index]['product_rating'] ?? '4.8',
// 'totalRatings': widget.numberOfRatings
'totalRatings':
provider[index]['product_rating_count'] ?? "124"
}),
child: Container(
margin: EdgeInsets.only(top: height * 0.02),
padding: const EdgeInsets.all(5),
width: double.infinity,
height: height * 0.15,
// color: Colors.yellow,
child: Stack(
children: [
Row(
children: [
Flexible(
flex: 1,
fit: FlexFit.tight,
child: Container(
height: double.infinity,
// width: width * 0.25,
decoration: const BoxDecoration(
color: Color.fromRGBO(86, 87, 84, 1),
borderRadius: BorderRadius.all(
Radius.circular(25))),
child: Center(
// child: Image.asset(provider[index]["image"]),
child: Image.network(
'https://achievexsolutions.in/current_work/eatiano/${provider[index]['product_image']}')),
),
),
SizedBox(width: width * 0.02),
Flexible(
flex: 3,
fit: FlexFit.tight,
child: Container(
height: height * 0.2,
// color: Colors.green,
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
// provider[index]["name"],
provider[index]['product_name'],
textScaleFactor: textScale,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold),
),
],
),
SizedBox(height: height * 0.001),
Expanded(
child: Text(
provider[index]
['product_description'],
textScaleFactor: textScale,
style: const TextStyle(
color: Colors.grey,
fontWeight: FontWeight.bold,
fontSize: 9),
),
)
],
),
),
)
],
),
Positioned(
top: height * 0.1,
left: width * 0.15,
child: Container(
width: width * 0.18,
height: height * 0.035,
padding: EdgeInsets.only(left: width * 0.01),
decoration: BoxDecoration(
color:
Theme.of(context).scaffoldBackgroundColor,
borderRadius:
BorderRadius.all(Radius.circular(10)),
border: Border.all(
width: 2,
color: const Color.fromRGBO(
161, 218, 46, 1))),
child: Row(
children: [
Text(
'₹',
textScaleFactor: textScale,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 15),
),
SizedBox(width: width * 0.02),
Text(
// provider[index]["price"],
provider[index]['product_selling_price'],
textScaleFactor: textScale,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 15),
)
],
),
),
)
],
),
),
),
itemCount: provider.length,
),
));
}
}
I just cannot figure out if the issue lies with the way I've filtered the data and displayed it or owing to the setState() or markNeedsBuild() called during build
error.
CodePudding user response:
Can you try put this code on initState
?
Provider.of<RestaurantProductProvider>(context, listen: false)
.productFilter(widget.category)
CodePudding user response:
If you clean the list before adding data to it you won't struggle with multiple data on rebuilds
Future<void> productFilter(String category) async {
_productList.clear(); //This line is new and cleans the List
_productList = _products.where((element) {.......}
You have to past the "clear()" method before you add the data to the list