Why is clearing a List using setState not working for clearing my ListView as well? - flutter

I have the following code for populating a list which I use for a ListView
List<dynamic> menuItemList =[];
Future<void> displayItems() async {
// I recently tried setState here after trying it onTap() of menu option
setState(() {
menuItemList.clear();
});
var getMenuIDs =
await _dbRef.child('Categories/$categoryPath').onValue.listen((event) {
if (event.snapshot.value != null) {
//fetching all available records in orderTracker
var data = event.snapshot.value as Map;
//adding records to list
data.entries.map((e) => itemIDList.add(e.value)).toList();
//cycling through the list and extracting the orderID
var listLength = itemIDList.length;
for (var i = 0; i < listLength; i++) {
var listItem = itemIDList[i].toString();
itemID = listItem.substring(9, 12);
// trackerID = listItem.substring(12, 15);
allMenuDetail(itemID);
}
} else {
//Empty
}
});
}
Future<void> allMenuDetail(String itemID) async {
//List will be empty here
_streamSubscriber =
await _dbRef.child('MenuItem/$itemID').onValue.listen((event) {
//For some reason it repopulates here
if (event.snapshot.value != null) {
var data = event.snapshot.value as Map;
var price = data['price'];
var itemName = data['itemName'];
var desc = data['description'];
var itemImg = data['img'];
var item = data['id'];
Map<String, dynamic> myMap = {
'price': price,
'itemName': itemName,
'desc': desc,
'img': itemImg,
'itemID': itemID
};
List<dynamic> shortList = [myMap];
menuItemList.addAll(shortList);
setState(() {});
} else {
print('*********************=> HERE ');
}
});
}
I then display the menu item as follows:
ListView.builder(
itemCount: menuItemList.length,
itemBuilder: (context, index) {
final item = menuItemList[index];
var price = item['price'];
var itemName = item['itemName'];
var desc = item['desc'];
var itemImg = item['img'];
var itemID = item['itemID'];
return InkWell(
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => ItemPageState(
// path: categoryPath,
itemName: itemName,
desc: desc,
price: price,
itemImg: itemImg,
id: itemID)));
},
child: Card(
color: Colors.white,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ClipRect(
child: Align(
alignment: Alignment.center,
widthFactor: 0.8,
child: Image(
image: AssetImage(itemImg),
height: 100,
width: 150,
fit: BoxFit.cover,
),
)),
const SizedBox(width: 30),
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
itemName,
style: const TextStyle(
color: Colors.black, fontSize: 25),
),
Text(
'Price: R' +
price.toString(), //+ price.toString(),
style: const TextStyle(
color: Colors.black, fontSize: 18),
),
],
),
const SizedBox(width: 30),
]),
));
},
)
I have a menu at the top of my page which will used to navigate to different categories of food. Once one of the icons is clicked I am able to fetch the relevant menu items but the list doesn't clear properly. I have use prints and it says the list has been emptied but on the GUI it just adds the other menu items at the bottom of the one first loaded
this is my menu code
Container(
height: 100,
child: ListView.separated(
padding: EdgeInsets.all(10),
separatorBuilder: (context, _) => const SizedBox(
width: 12,
),
scrollDirection: Axis.horizontal,
itemCount: categoryMenuList.length,
itemBuilder: (context, index) {
final catItem = categoryMenuList[index];
var catIgm = catItem['img'];
var title = catItem['title'];
var path = catItem['path'];
return Container(
width: 95,
height: 100,
child: Column(
children: [
Expanded(
child: AspectRatio(
aspectRatio: 4 / 3,
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Material(
child: Ink.image(
image: AssetImage(catIgm),
fit: BoxFit.cover,
child: InkWell(
onTap: () {
//I did try set state here as well
categoryPath = path;
pageTitle = title;
displayItems();
setState(() {});
refreshPage();
},
),
))))),
const SizedBox(
height: 4,
),
Text(
title,
style: const TextStyle(
color: Colors.black, fontSize: 20),
)
],
),
);
}),
),
Sorry for the long winded post but I can't figure out where I'm going wrong

Related

How do I add pictures to my menu items (which are Card widgets) using a StreamBuilder in flutter?

I first got the stream builder to work with a listTile but now I want to use a card instead.
I am using a firebase real-time database and have stored the pictures in the menuItem tree as follows.
menuItem
J1
description:"Tasty milk shake!"
img:"assets/images/milkshake.jpg"
itemName:"Milk Shake"
price:20
The page I am working with is rewards which stores the menut item ID as part of the record so I can retrieve the full details of the reward item.
here is the code for the stream builder I have used for the reward Card items:
body: Center(
child: StreamBuilder(
stream: _dbRef.child('Rewards').onValue,
builder: (context, snapshot) {
final tilesList =
<InkWell>[];
if (snapshot.hasData) {
final myRewards = Map<String, dynamic>.from(
(snapshot.data! as DatabaseEvent).snapshot.value
as Map<dynamic, dynamic>);
myRewards.forEach((key, value) {
getImg();
print("++++++++++++++++++++++++++++++++++ => $publicImg");
final nextReward = Map<String, dynamic>.from(
value);
publicID = nextReward['menuItemID'];
final rewardTile = InkWell(
onTap: () async {
final pointCost = nextReward[
'pointCost'];
final id = nextReward[
'menuItemID'];
final snapshot = await _dbRef.child('menuItem/$id').get();
if (snapshot.exists) {
final details = Map<String, dynamic>.from(
snapshot.value! as Map<Object?, Object?>);
pass to next page
String itemName = details['itemName'];
String desc = details['description'];
int cost = pointCost;
String itemImg = details['img'];
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => RewardItemPage(
itemName: itemName,
desc: desc,
rewardPoints: cost.toString(),
itemImg: itemImg,
)));
}
},
child: Card(
margin: const EdgeInsets.all(10),
color: Colors.white,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ClipRect(
child: Align(
alignment: Alignment.center,
widthFactor: 0.8,
child: Image(
image: AssetImage(publicImg),
height: 100,
fit: BoxFit.cover,
),
)),
const SizedBox(width: 30),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.center,
children: [
Text(
nextReward['rewardName'],
style: const TextStyle(
color: Colors.black, fontSize: 25),
),
Text(
'Points: ' +
nextReward['pointCost'].toString(),
style: const TextStyle(
color: Colors.black, fontSize: 18),
),
],
),
const SizedBox(width: 30),
Container(
alignment: Alignment.centerRight,
child: GFIconButton(
onPressed: () {},
icon: const Icon(
Icons.favorite_outline,
color: Colors.black,
),
type: GFButtonType.transparent,
),
)
]),
));
This is the code for my method I am using to get the images for the Card widget:
Future<String> getImg() async {
final snap = await _dbRef.child('menuItem/$publicID/img').get();
if (snap.exists) {
publicImg = snap.value.toString();
print('NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN => $publicImg');
}
return publicImg;
}
My code returns no pictures initially and upon a hot reload it gets pictures for only one item and sets it as the image for all card items.
Sorry for the long winded coded but I am struggling with this.

How to increase quantity of same item click in Flutter (Bloc)

In HomePage.dart i am fetching an api data by using http package and flutter_bloc for the architecture. Now once i get the data and if i select any of the item in HomePage.dart then it will be added in MenuCart.dart. Now the problem i am facing is
If i click the same item more than one time then it shows duplicate card widget.
In MenuCart.dart i have added two buttons + & - and i increases the quantity to 3 then i decided to delete the item. Now i go back to previous screen and again i add that same item and if i go to MenuCart.dart then quantity displays 4.
What i am Expecting
It should not generate duplicate card instead quantity should increment.
After i delete an item and again add the same item then quantity must show 1.
In HomePage.dart i show very limited amount of code because of lengthy, but i have added the code of bloc and MenuCart.dart.
HomePage.dart
GestureDetector(
onTap: () {
BlocProvider.of<MenuCartBloc>(context)
.add(AddToCart(data[index])); // Here adding an item in MenuCart
},
child: Container(
child: Center(
child: Padding(
padding: const EdgeInsets.all(4.5)
child: Text('ADD'),
),
),
),
);
DishMenuTypesId.dart (Model)
class DishMenuTypesIdData {
String? id;
int? status;
int? quantity;
String? dishPrice;
String? photo;
DishMenuTypesIdData({
this.id,
this.status,
this.quantity = 1,
this.dishPrice,
this.photo,
});
DishMenuTypesIdData.fromJson(Map<String, dynamic> json) {
id = json['id']?.toString();
status = json['status']?.toInt();
quantity = 1;
dishPrice = json['dish_price']?.toString();
photo = json['photo']?.toString();
}
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data['id'] = id;
data['status'] = status;
data["quantity"] = quantity;
data['dish_price'] = dishPrice;
data['photo'] = photo;
return data;
}
}
class DishMenuTypesId {
String? message;
List<DishMenuTypesIdData?>? data;
DishMenuTypesId({
this.message,
this.data,
});
DishMenuTypesId.fromJson(Map<String, dynamic> json) {
message = json['message']?.toString();
if (json['data'] != null) {
final v = json['data'];
final arr0 = <DishMenuTypesIdData>[];
v.forEach((v) {
arr0.add(DishMenuTypesIdData.fromJson(v));
});
this.data = arr0;
}
}
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data['message'] = message;
if (this.data != null) {
final v = this.data;
final arr0 = [];
v!.forEach((v) {
arr0.add(v!.toJson());
});
data['data'] = arr0;
}
return data;
}
}
MenuCartBloc.dart
class MenuCartBloc extends Bloc<MenuCartEvent, MenuCartState> {
MenuCartBloc() : super(MenuLoaded([]));
List<DishMenuTypesIdData> cart = [];
#override
Stream<MenuCartState> mapEventToState(
MenuCartEvent event,
) async* {
yield MenuLoading();
try {
if (event is AddToCart) {
final itemExist = cart.where((e) => e.id == event.dish.id);
if (itemExist.isNotEmpty) {
print("Not Empty"); // Here i am expecting to show qty 2 if i click an item more than 1 time
} else {
print("Empty");
cart.add(event.dish);
}
} else if (event is DeleteToCart) {
cart.remove(event.dish);
} else if (event is ClearAllCart) {
cart = [];
}
yield MenuLoaded(cart);
} catch (e) {
yield MenuFailed(e.toString());
}
}
}
MenuCartEvent.dart
abstract class MenuCartEvent {}
class AddToCart extends MenuCartEvent {
late final DishMenuTypesIdData dish;
AddToCart(this.dish);
}
class DeleteToCart extends MenuCartEvent {
late final DishMenuTypesIdData dish;
DeleteToCart(this.dish);
}
class ClearAllCart extends MenuCartEvent {}
MenuCartState.dart
abstract class MenuCartState {}
class MenuLoading extends MenuCartState {}
class MenuLoaded extends MenuCartState {
final List<DishMenuTypesIdData> cart;
MenuLoaded(this.cart);
}
class MenuFailed extends MenuCartState {
final String message;
MenuFailed(this.message);
}
MenuCart.dart
Widget build(BuildContext context) {
var height = MediaQuery.of(context).size.height;
var width = MediaQuery.of(context).size.width;
var lang = translator.activeLanguageCode;
return Container(
child: Padding(
padding: const EdgeInsets.only(top: 20.0, left: 10.0, right: 10.0),
child: BlocBuilder<MenuCartBloc, MenuCartState>(
builder: (context, state) {
DishMenuTypesIdData _cart = DishMenuTypesIdData();
if (state is MenuLoading) {
return PlatformCircularProgressIndicator();
} else if (state is MenuLoaded) {
return SingleChildScrollView(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Your Reservation Details'.tr(),
style: TextStyle(
color: TuxedoColor.blackColor,
fontSize: lang == "en" ? 14.0 : 15.0,
fontFamily: lang == "en"
? 'OpenSansBold'
: 'ArabicSemiBold'),
),
GestureDetector(
onTap: () {
BlocProvider.of<MenuCartBloc>(context)
.add(ClearAllCart());
},
child: Row(
children: [
Icon(
Icons.close,
color: TuxedoColor.redColor,
size: 15.0,
),
Text(
'Clear All'.tr(),
style: TextStyle(
color: TuxedoColor.redColor,
fontSize: 12.0,
fontFamily: lang == "en"
? 'OpenSansRegular'
: 'ArabicLight'),
),
],
),
)
],
),
SizedBox(
height: 25.0,
),
Container(
height: height * 0.3,
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: state.cart.length,
itemBuilder: (BuildContext context, int index) {
_cart = state.cart[index];
return Dismissible(
key: UniqueKey(),
background: Container(
alignment: AlignmentDirectional.centerEnd,
color: TuxedoColor.redBrightColor,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(
Icons.delete,
color: TuxedoColor.whiteColor,
),
),
),
onDismissed: (direction) {
BlocProvider.of<MenuCartBloc>(context)
.add(DeleteToCart(_cart));
setState(() {
state.cart.removeAt(index);
});
},
direction: DismissDirection.endToStart,
child: Card(
child: Row(
children: [
Stack(
children: [
ClipRRect(
borderRadius:
BorderRadius.circular(10.0),
child: CachedNetworkImage(
imageUrl:
_cart.photo!,
placeholder: (context, url) => Center(
child:
PlatformCircularProgressIndicator()),
errorWidget:
(context, url, error) =>
Icon(Icons.error),
width: width * 0.25,
height: height * 0.1,
fit: BoxFit.fill,
),
),
Positioned(
bottom: 0,
right: 0,
child: Container(
decoration: BoxDecoration(
color: TuxedoColor.redColor,
borderRadius: lang == "en"
? BorderRadius.only(
bottomRight:
Radius.circular(
10),
)
: BorderRadius.only(
bottomLeft:
Radius.circular(
10),
)),
height: 25.0,
width: 45.0,
child: Padding(
padding: const EdgeInsets.only(
left: 3.0, right: 3.0),
child: FittedBox(
fit: BoxFit.fitWidth,
child: Text(
'${_cart.dishPrice!} \SR',
style: TextStyle(
color: TuxedoColor
.whiteColor,
fontFamily: lang == "en"
? 'OpenSansSemiBold'
: 'ArabicSemiBold'),
),
),
),
),
)
],
),
Padding(
padding:
const EdgeInsets.only(left: 10.0),
child: Column(
children: [
Container(
width: width * 0.6,
child: Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Container(
height: 25.0,
child: Row(
children: [
MaterialButton(
color: TuxedoColor
.redColor,
shape: CircleBorder(),
onPressed: () {
setState(() {
var minus = state
.cart[index]
.quantity;
if (minus !=
null) {
minus--;
state
.cart[index]
.quantity = minus;
}
});
},
child: Text(
'-',
style: TextStyle(
color: Colors
.white,
fontSize: 20),
),
),
Text(_cart.quantity
.toString()),
MaterialButton(
color: TuxedoColor
.redColor,
shape: CircleBorder(),
onPressed: () {
setState(() {
var add = state
.cart[index]
.quantity;
if (add != null) {
add++;
state
.cart[index]
.quantity = add;
}
});
},
child: Text(
'+',
style: TextStyle(
color: Colors
.white,
fontSize: 20),
),
),
],
),
),
GestureDetector(
onTap: () {
BlocProvider.of<
MenuCartBloc>(
context)
.add(DeleteToCart(
state.cart[
index]));
},
child: Icon(
Icons.delete,
color:
TuxedoColor.redColor,
),
)
],
),
),
SizedBox(
height: 15.0,
)
],
),
),
],
),
),
);
}),
),
],
),
);
} else if (state is MenuFailed) {
return Center(child: Text(state.message));
}
return Container();
},
)),
);
}
Something like that should work:
final itemExist = cart.firstWhere((e) => e.id == event.dish.id, orElse: () => null);
if (itemExist != null) {
itemExist.quantity++;
print("Not Empty"); // Here i am expecting to show qty 2 if i click an item more than 1 time
} else {
print("Empty");
cart.add(event.dish);
}
Basically, we need to check whether the cart already has a dish with the same id or not. If the cart contains the dish with this id we increment dish quantity else we just add a dish to the cart.
Besides that, I see a potential problem with your code. Bloc to work properly requires either creating new instances of models or properly implementing of equals and hashCode. So even the code above may not work properly due to this problem. Be aware of it. You can fix it using this package.

I am having trouble iterating through an array of objects

I am trying to go through an array of objects, I stored them in my SharedPreferences where I go the data from firebase and add the quantity for each object, now I only want to display the title, price, and quantity of the product in the cart. I was able to pull all the values belonging to the product to the cart screen but how to loop through the nested values in the cart screen is the problem. please can anyone help me still learning more on flutter?
Cart screen
#override
Widget build(BuildContext context) {
SharedPreferences prefs = SharedPreferences.getInstance() as SharedPreferences;
var cart = prefs.getStringList('userCart');
return Row(
children: [
SizedBox(
width: getProportionateScreenWidth(88),
child: AspectRatio(
aspectRatio: 0.88,
child: Container(
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
color: Color(0XFFF5F6F9),
borderRadius: BorderRadius.circular(15),
),
child: Image.network(cart![0]),
// child: Image.network(cart.product.images[0]),
),
),
),
SizedBox(
width: getProportionateScreenWidth(20),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
cart.first,
// cart.product.title,
style: TextStyle(fontSize: 16, color: Colors.black),
maxLines: 2,
),
const SizedBox(
height: 10,
),
Text.rich(
TextSpan(
text: "\$${cart.product.price}",
style: TextStyle(
color: kPrimaryColor,
),
children: [
TextSpan(
text: " x${cart.numOfItem}",
style: TextStyle(
color: kTextColor,
),
),
],
),
),
],
)
],
);
}
Storing the data from firebase and adding quantity
Future<void> checkItemInCart(
Product product, int quantity, BuildContext context) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
// convert to map
var product_str = product.toMap();
// combine product with quantity
String productWithQuantity =
product_str.toString() + '-quantity-' + quantity.toString();
// check if project exists first
List<String> userCartPref = (prefs.getStringList('userCart') ?? <String>[]);
['Product-quantity-2'];
/*
update {
check if found
}
*/
List<String> temp = (prefs.getStringList('userCart') ?? <String>[]);
// add f
//userCartPref ['iphone 1','laptop 3'];
// temp ['laptop 3'];
var foundInCart = false;
for (var i = 0; i < userCartPref.length; i++) {
var item = userCartPref[i];
var items = item.split('-quantity-'); //list [product,quantity]
var old_product = items[0];
var old_qty = items[1];
if (old_product.contains(product.pid)) {
foundInCart = true;
// product exists
// delete the current item
temp.removeAt(i);
// set pref to temp
prefs.setStringList('userCart', temp);
// sum the quantity 2 1
String finalQuantity = (quantity + int.parse(old_qty)).toString();
// create string for pref with the updated quantity
String updatedProductWithQuantity =
product_str.toString() + '-quantity-' + finalQuantity;
//add item with the updated quantity iphone 2
addItemToCart(updatedProductWithQuantity, context);
showSnackBar(context, "Quantity has been updated successfully");
break;
}
}
if (userCartPref.length == 0 || foundInCart == false) {
addItemToCart(productWithQuantity, context);
showSnackBar(context, "Product added successfully to cart");
}
await getProducts();
}
Future<void> addItemToCart(String product, BuildContext context) async {
// await clearPref();
print("inside");
SharedPreferences prefs = await SharedPreferences.getInstance();
List<String> tempCartList = (prefs.getStringList('userCart') ?? <String>[]);
// print(tempCartList);
tempCartList.add(product);
prefs.setStringList('userCart', tempCartList);
}
Future<void> getProducts() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
List<String> tempCartList =
(preferences.getStringList('userCart') ?? <String>[]);
for (var i = 0; i < tempCartList.length; i++) {
var item = tempCartList[i];
var items = item.split('-quantity-');
var product_ = items[0];
var quantity_ = items[1];
}
}
you can use ListView.builder or GridView.builder to iterate over the array and render them on screen. So if I use ListView.builder, my code in cart screen would look like:
return ListView.builder(
itemCount: cart.length, //length of cart
itemBuilder: (context, index) {
return Row(
children: [
SizedBox(
width: getProportionateScreenWidth(88),
child: AspectRatio(
aspectRatio: 0.88,
child: Container(
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
color: Color(0XFFF5F6F9),
borderRadius: BorderRadius.circular(15),
),
child: Image.network(cart![index]),
// child: Image.network(cart.product.images[0]),
),
),
),
SizedBox(
width: getProportionateScreenWidth(20),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
cart[index].product.title,
// cart.product.title,
style: TextStyle(fontSize: 16, color: Colors.black),
maxLines: 2,
),
const SizedBox(
height: 10,
),
Text.rich(
TextSpan(
text: "\$${cart[index].product.price}",
style: TextStyle(
color: kPrimaryColor,
),
children: [
TextSpan(
text: " x${cart[index].numOfItem}",
style: TextStyle(
color: kTextColor,
),
),
],
),
),
],
)
],
);
},
);

Flutter FutureBuilder calling function continuously

I have simple function which is calling data from firestore and filtering data. But issue is my futurebuilder keeps on loader situation (Data is called successfully i can see in console but now showing in future) I think its because my fucntion is calling in loop or something i have try to print something in my function which indicates me that my function is not stopping and thats why i think my futureBuilder keeps on loading.
My code
Future<List> getCustomerList() async {
print('calling');
String uUid1 = await storage.read(key: "uUid");
String uName1 = await storage.read(key: "uName");
String uNumber1 = await storage.read(key: "uNumber");
setState(() {
uUid = uUid1;
uName = uName1;
uNumber = uNumber1;
});
CollectionReference _collectionRef =
FirebaseFirestore.instance.collection('Customers');
QuerySnapshot querySnapshot = await _collectionRef.get();
// Get data from docs and convert map to List
List allData = querySnapshot.docs
.where((element) => element['sellerUID'] == uUid)
.map((doc) => doc.data())
.toList();
double gGive = 0;
double gTake = 0;
double gCal = 0;
for (int i = 0; i < allData.length; i++) {
// print(allData[i]);
// print('give ${double.parse(allData[i]['give'].toString()) }');
// print('take ${double.parse(allData[i]['take'].toString()) }');
double.parse(allData[i]['give'].toString()) -
double.parse(allData[i]['take'].toString()) >
0
? gGive += double.parse(allData[i]['give'].toString()) -
double.parse(allData[i]['take'].toString())
: gTake += double.parse(allData[i]['give'].toString()) -
double.parse(allData[i]['take'].toString());
}
// print(gGive);
// print(gTake);
setState(() {
Gtake = gGive.toString().replaceAll("-", "");
Ggive = gTake.toString().replaceAll("-", "");
});
if (greenBox) {
var check = allData.where((i) => i['take'] > i['give']).toList();
return check;
} else if (redBox) {
var check = allData.where((i) => i['give'] > 1).toList();
return check;
} else {
return allData;
}
}
And my futureBuilder look like this
Expanded(
child: Container(
height: Height * 0.5,
child: FutureBuilder(
future: getCustomerList(),
builder: (context, snapshot) {
if (snapshot.hasData) {
list = snapshot.data;
return SingleChildScrollView(
child: Column(
children: [
Container(
height: Height * 0.5,
child: ListView.builder(
shrinkWrap: true,
itemCount: list.length,
itemBuilder:
(BuildContext context,
int index) {
var showThis = list[index]
['give'] -
list[index]['take'];
return list[index]
['customerName']
.toString()
.contains(searchString)
? GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
CustomerData(
data: list[
index])),
);
},
child: Padding(
padding:
const EdgeInsets
.only(
left: 13,
right: 13),
child: Container(
decoration:
BoxDecoration(
border: Border(
top: BorderSide(
color: Colors
.grey,
width:
.5)),
),
child: Padding(
padding:
const EdgeInsets
.all(
13.0),
child: Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Row(
children: [
CircleAvatar(
child:
Text(
list[index]['customerName'][0]
.toString(),
style:
TextStyle(fontFamily: 'PoppinsBold'),
),
backgroundColor:
Color(0xffF7F9F9),
),
SizedBox(
width:
20,
),
Text(
list[index]['customerName']
.toString(),
style: TextStyle(
fontFamily:
'PoppinsMedium'),
),
],
),
Text(
'RS ${showThis.toString().replaceAll("-", "")}',
style: TextStyle(
fontFamily:
'PoppinsMedium',
color: list[index]['give'] - list[index]['take'] <
0
? Colors.green
: Colors.red),
),
],
),
),
),
),
)
: Container();
},
),
)
],
),
);
} else
return Center(
heightFactor: 1,
widthFactor: 1,
child: SizedBox(
height: 70,
width: 70,
child: CircularProgressIndicator(
strokeWidth: 2.5,
),
),
);
}),
),
),
I am damn sure its because futurebuilder keeps calling function which is returning data but because of keeps calling functions my Futurebuilder keeps showing loading.
You should not call setState inside the future that you are giving to the FutureBuilder.
The state actualization will cause the FutureBuilder to re-build. Meaning triggering the future again, and ... infinite loop !

Flutter setState list isnt updating Listviewbuilder

I have a simple list and I am filter the list by date range picker. All is working fine its showing result also that list is filter success but on ListView builer its showing this error.
Class 'WhereIterable<Object>' has no instance method '[]'.
My code
Future<List> getCustomerList() async {
print(widget.data);
// print('check running');
final storage = new FlutterSecureStorage();
String uUid = await storage.read(key: "uUid");
CollectionReference _collectionRef =
FirebaseFirestore.instance.collection('Transaction');
QuerySnapshot querySnapshot = await _collectionRef.get();
// Get data from docs and convert map to List
List allData = querySnapshot.docs
.where((element) => element['CustomerID'] == widget.data['customerID'])
.map((doc) => doc.data())
.toList();
print(allData);
restore = allData;
backrestore = allData;
setState(() {
show = true;
});
}
Padding(
padding: const EdgeInsets.all(8.0),
child: GestureDetector(
onTap: () async {
final List<DateTime> picked =
await DateRangePicker.showDatePicker(
context: context,
initialFirstDate: new DateTime.now(),
initialLastDate: (new DateTime.now())
.add(new Duration(days: 6)),
firstDate: new DateTime(2015),
lastDate:
new DateTime(DateTime.now().year + 2));
if (picked != null && picked.length == 2) {
print(picked);
selectedDate = picked[0];
DateTime endDate = picked[1];
List<DateTime> days = [];
var filterData = backrestore;
DateTime date = DateTime.parse(
filterData[0]['lastupdate'].toDate().toString());
print(date);
for (int i = 0;
i <= endDate.difference(selectedDate).inDays;
i++) {
days.add(selectedDate.add(Duration(days: i)));
}
print(days);
final filteredItemsToAdd = filterData.where((item) =>
days.contains(DateTime.parse(
item['lastupdate'].toDate().toString())));
print(filteredItemsToAdd.length);
print(filteredItemsToAdd);
setState(() {
restore = filteredItemsToAdd;
});
}
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Color(0xffE5E7E9)
),child: Padding(
padding: const EdgeInsets.all(13.0),
child: Text('Date'),
))),
),
Container(
height: Height * 0.5,
child: ListView.builder(
shrinkWrap: true,
itemCount: restore.length,
itemBuilder: (BuildContext context, int index) {
DateTime date = DateTime.parse(
restore[index]['lastupdate'].toDate().toString());
// print(DateFormat('dd-MMM-yyy').format(date)); // prints 2019-04-16
// print('List length ${snapshot.data.length}');
return Padding(
padding: const EdgeInsets.only(left: 13, right: 13),
child: Container(
decoration: BoxDecoration(
border: Border(
top: BorderSide(color: Colors.grey, width: .5)),
),
child: Padding(
padding: const EdgeInsets.all(13.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
DateFormat('dd MMM yyy').format(date).toString(),
style: TextStyle(fontFamily: 'PoppinsMedium'),
),
Row(
children: [
Text(
restore[index]['give'].toString(),
style: TextStyle(
fontFamily: 'PoppinsMedium',
color: Colors.green),
),
SizedBox(
width: 60,
),
Text(
restore[index]['take'].toString(),
style: TextStyle(
fontFamily: 'PoppinsMedium',
color: Colors.red),
),
],
)
],
),
),
),
);
},
),
),
You can see I am fetching list then saving on object. And on DateRange picker I am changing the new value by setState but dont know why its showing this error.
I believe this code causes the problem
List allData = querySnapshot.docs
.where((element) => element['CustomerID'] == widget.data['customerID'])
.map((doc) => doc.data())
.toList();
element has type QueryDocumentSnapshot. Replace element['CustomerID'] by element.data()['customID'].