how to display updated value of a list in a gridTile - flutter

I have a class called Statics, which contains List variables that store number of values.
class Statics {
//24 is the maximum number of cells in gridView.
static List heatMaps = List.filled(24, "miner ID", growable: false);
static int selectedHeatMap = -1;
static List selectedMinerTemp = List.filled(24,0,growable: false);
static List isPressed = List.filled(24, false);
}
in another widget i have gridView to display the data from the lists of Statics,
the data stored in Statics changes every few seconds, when i press a tile and get the data it shows the updated data but it does not automatically update the data and you must click on the desired tile item (from the list of data) again.
here is my gridView:
GridView.count(
crossAxisCount: 3,
children: List.generate(
selectItemsNumber(selectedValue3), (index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: GestureDetector(
onTap: () {
/////////////here////////////
Statics.selectedHeatMap = index;
Navigator.pushNamed(context, "/listOfMiners",
arguments: selectedValue2);
},
child: Container(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
SizedBox(
height: 10,
),
Text(
Statics.heatMaps[index],
// widget.selectedMiner.toString(),
style: TextStyle(fontSize: 13),
),
Text(
Statics.selectedMinerTemp[index]
.toString(),
// widget.selectedMiner.toString(),
style: TextStyle(fontSize: 13),
),
],
),
),
width: 100,
height: 100,
color:Statics.isPressed[index] == false ? Colors.white : colorPicker(
Statics.selectedMinerTemp[index])),
),
);
}),
),
and this my method to catch the data:
Stream<GroupsStates?> streamItems() async* {
yield* Stream.periodic(Duration(seconds: 5), (_) async {
final client = ClientChannel(
LoginState.ipServerController.text,
port: 10000,
options: ChannelOptions(
credentials: ChannelCredentials.insecure(),
),
);
final stub = EngineClient(client);
try {
var result = (await stub.groupsStatesAll(Void()));
return result;
} catch (e) {
print('Cuaght error: $e');
} }).asyncMap((event) async => await event);
}

Related

List.add not adding element to a boolean list in flutter

I have two Lists, imageFileList and values
// contains a list of image files which are captured from the camera page
List<File> imageFileList = widget.imageFileList;
// Values list is filled with boolean values which are defaulted to false and contains the same length as imageFileList
List<bool> values = widget.values;
the imageFileList gets a new File added when user selects a picture from the gallery, and i want to simultaneously add another bool entry to the values List, as such -
Future pickImage() async {
try {
final image =
await ImagePicker().pickImage(source: ImageSource.gallery);
if (image == null) return;
final imageTemp = File(image.path);
setState(() {
// adding a new image to imageFileList
imageFileList.add(imageTemp);
// adding a new bool to values List
values.add(false);
});
// rebuildAllChildren(context);
} catch (e) {
print(e);
}
}
When i use pickImage(), the file gets added to the imageFileList, but the false bool is not getting added to the values List.
debug output shows this line when i print the lengths of imageFileList and values List -
print("imagelist = " +
imageFileList.length.toString());
print("values = " +
values.length.toString());
// debug output
I/flutter (20710): imagelist = 5
I/flutter (20710): values = 4
any suggestions on what might be wrong?
here is the full code -
Widget build(BuildContext context) {
// contains a list of image files which are captured from the camera page
List<File> imageFileList = widget.imageFileList;
// Values list is filled with boolean values which are defaulted to false and contains the same length as imageFileList
List<bool> values = widget.values;
Future pickImage() async {
try {
final image =
await ImagePicker().pickImage(source: ImageSource.gallery);
if (image == null) return;
final imageTemp = File(image.path);
setState(() {
imageFileList.add(imageTemp);
values.add(false);
});
// rebuildAllChildren(context);
} catch (e) {
print(e);
}
}
getStoragePermissionStatus() async {
await Permission.camera.request();
var status = await Permission.storage.status;
if (status.isGranted) {
log('Camera Permission: GRANTED');
setState(() {
_isStoragePermissionGranted = true;
});
// asking the user to select an image IF they grant storage permission
await pickImage();
print(_isStoragePermissionGranted);
} else {
log('Camera Permission: DENIED');
}
}
return WillPopScope(
onWillPop: (() => onWillPop(context)),
child: Scaffold(
backgroundColor: Color(lightBlueColor),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.all(8.0),
child: Text(
'Captures',
style: TextStyle(
fontSize: 32.0,
color: Colors.white,
),
),
),
Container(
height: returnDeviceHeight(context) / 1.2,
width: returnDeviceWidth(context),
child: ListView.builder(
itemCount: imageFileList.length,
itemBuilder: (BuildContext context, int index) {
String key = imageFileList[index].toString();
return Column(
children: <Widget>[
Container(
width: returnDeviceWidth(context) - 10,
height: returnDeviceHeight(context) / 2,
decoration: BoxDecoration(
borderRadius:
BorderRadius.all(Radius.circular(8.0)),
color: Colors.black,
border: Border.all(
color: values[index] ? Colors.green : Colors.red,
width: 4,
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Center(
child: InkWell(
onTap: () {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => PreviewScreen(
fileList: imageFileList,
imageFile: imageFileList[index],
),
),
);
},
child: Image.file(
imageFileList[index],
fit: BoxFit.cover,
),
),
),
),
],
),
),
Divider(
height: 2.0,
),
],
);
},
),
),
ReturnSizedBox10(),
InkWell(
onTap: () async {
await getStoragePermissionStatus();
},
child: Container(
width: returnDeviceWidth(context) - 10,
height: 50,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(8.0)),
color: Color(darkBlueColor),
),
child: Center(
child: LightBluefontstyleMont(
text: "Pick an image from gallery", size: 20),
),
),
),
InkWell(
onTap: () async {
print("imagelist = " +
imageFileList.length.toString());
print("values = " +
values.length.toString());
},
child: Container(
width: returnDeviceWidth(context) - 10,
height: 50,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(8.0)),
color: Colors.white,
),
child: Center(
child: LightBluefontstyleMont(text: "Done", size: 20),
),
),
)
],
),
),
),
);
}
I have tried to with the setState and without it, however both times, only the imageFileList gets the new entry added.
I need to add a new entry to values List when the imageFileList gets a new entry added to it.
I suspect its to do with the widget.values being passed by pointer.
what u can do is copy the value and then assigning it to List<bool> values variable.
change this:
List<bool> values = widget.values;
to this:
List<bool> values = widget.values.toList();
Well, turns out every time I add a new image to the imageFileList, the values List gets reset; to the value it had in widget.values.
Fixed it by using initState -
late List<File> imageFileList;
late List<dynamic> values;
#override
void initState() {
// contains a list of image files which are captured from the camera page
imageFileList = widget.imageFileList;
// Values list is filled with boolean values which are defaulted to false and contains the same length as imageFileList
values = widget.values.toList();
// TODO: implement initState
super.initState();
}

How to implement objectbox into flutter appilcation

I am working on an app, and need help trying to change my save method to object box. I have been using path_provider to save data but want to switch it to the object box database but I am struggling to do it. The goal is to have each timer button be clicked to record a time and present them back to the user to see how long each task/button took. Below is my code for my ViewModel and my timer_page dart files.
#Entity()
class StudyViewModel {
static List<Study> studies = [];
static List<ValueChanged<ElapsedTime>> timerListeners =
<ValueChanged<ElapsedTime>>[];
static Stopwatch stopwatch = new Stopwatch();
/// load from file...
static Future load() async {
try {
File file = await getFile();
String studiesJson = await file.readAsString();
if (studiesJson.isNotEmpty) {
List studiesParsed = json.decode(studiesJson);
studies = studiesParsed.map((i) => Study.fromJson(i)).toList();
}
} catch (e) {
print(e);
}
}
static Future<File> getFile() async {
final directory = await getApplicationDocumentsDirectory();
final path = directory.path;
return File('$path/studies.json');
}
static Future saveFile() async {
File file = await getFile();
file.writeAsString(json.encode(studies));
}
SizedBox(
height: 600,
child: GridView.count(
primary: false,
padding: const EdgeInsets.all(20),
crossAxisSpacing: 10,
mainAxisSpacing: 10,
crossAxisCount: 3,
children: widget.tasks.map((element) {
final isActive = activeTask != null && activeTask == element;
return GestureDetector(
onTap: () {
// set active task or toggle is active
if (isActive) {
setState(() {
activeTask = null;
StudyViewModel.stopwatch.start();
disable = true;
});
} else {
setState(() {
activeTask = element;
StudyViewModel.stopwatch.stop();
disable = false;
});
}
},
child: Container(
color: isActive ? Colors.amber : Colors.green,
padding: EdgeInsets.all(8),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
element.name,
style: TextStyle(color: Colors.white, fontSize: 25),
textAlign: TextAlign.center,
),
Text(
element.elapsedTime
)
]
),
),
);
}).toList(),
),
),
Expanded(
child: timerText,
),
Expanded(
flex: 0,
child: Padding(
padding: const EdgeInsets.only(bottom: 24.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
RaisedButton(
onPressed: disable
? null
: () {
setState(() {
StudyViewModel.stopwatch.reset();
});
},
color: Colors.red,
padding: EdgeInsets.symmetric(
horizontal: 60.0,
vertical: 20.0,
),
child: Text(
"Reset",
style: TextStyle(fontSize: 20.0, color: Colors.white),
),
),
RaisedButton(
onPressed: disable
? null
: () async {
await showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Text('Do you wish to save a time study?'),
actions: <Widget>[
FlatButton(
child: Text('Accept'),
onPressed: () async {
StudyViewModel.saveFile();
Navigator.of(context).pop();
},
),
FlatButton(
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
]
);
}
);

Flutter Tried calling: [] error on list builder

I am showing list-builder i need to show just a static data right now I am just check itemCount right now later'll show data but it's showing error .
Here is my code
class _OrderPageState extends State<OrderPage> {
bool showCards = false;
var data;
#override
void initState() {
this.getOrders();
}
getOrders() async{
final storage = new FlutterSecureStorage();
String userId = await storage.read(key: "_userID");
String url =
'http://retailapi.airtechsolutions.pk/api/orders/customer/${userId}/0';
print(url);
http.Response res = await http.get(
url,
);
var data = json.decode(res.body.toString());
print(data);
if(data['description'].toString() == "Success"){
print(data['Orders']);
print(data['Orders'].length); //its printing 6 here
setState(() {
showCards = true;
});}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Order', style: Theme.of(context).textTheme.headline4),
),
body: showCards ? Container(
child: ListView.builder(
itemCount: data['Orders'].length,
shrinkWrap: true,
padding: EdgeInsets.symmetric(horizontal: 18.0, vertical: 20.0),
itemBuilder: (context, index) {
var order = orderList[index];
return SideInAnimation(index, child:GestureDetector(
onTap: () {
// Get.to(OrderDetailPage(order: order));
},
child: Container(
width: double.infinity,
padding: EdgeInsets.all(12.0),
margin: EdgeInsets.only(bottom: 15.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15.0),
border: Border.all(color: Theme.of(context).accentColor),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(order.id,
style: Theme.of(context)
.textTheme
.headline3
.copyWith(color: Theme.of(context).primaryColor)),
SizedBox(height: 12.0),
Text(order.dateOrder, style: Theme.of(context).textTheme.subtitle2),
Divider(),
orderCardItem(context,
title: "order.orderstatus", data: order.orderStatus),
SizedBox(height: 12.0),
orderCardItem(context,
title: "order.items",
data: "${order.totalItem} " + tr("order.itemspurchased")),
SizedBox(height: 12.0),
priceItem(context,
title: "order.price", data: "\$ ${order.totalPrice}"),
],
),
),
));
},
),
) : Container(),
);
}
}
In my function its printing the length by in List builder its showing an error that Tried calling: ` But on the print where API load it's showing the length 6.
First you need to initalize a list or array then simply add your data into that variable and then call this variable to your listview builder
var ordersData = [];
then your getData() method should be like this
getOrders() async {
...
if (data['description'].toString() == "Success") {
ordersData.add(data['Orders']); // Add your data to array or list
print(ordersData.length); //its printing 6 here
}
...
}
Here your Listview like this
ListView.builder(
itemCount: ordersData.length, // Here you need to pass this lenght
...
)

When I'm trying to remove items from the listview using provider it removed last element from the list in flutter

When I'm trying to remove item(like 0 index item) from this listview using provider it removed the last item from the list. while I'm deleting last element from the list successfully remove last item. I'm bit confusing why this kind of problem is happening to me.
Here I'm posting some code please check give your valuable suggestion. Also demonstrate on this video what issue is happening
Link:https://drive.google.com/file/d/1UYl8Z7vEj_tZCaYzqe0VqZL2iMla5nIZ/view?usp=sharing
Expected result: Whenever user press delete button then delete that particular row(item).
Delete method:- This is the delete method It'll be call when user press delete button from the list.
Future<void> acceptdeclinerequest(String requestStatus,int requestId) async{
String token = await CustomPreferences.getpreferences('token');
Map<String, String> requestHeaders;
if (token.isNotEmpty) {
requestHeaders = {
'Accept': 'application/json',
'Authorization': 'Bearer ' + token
};
} else {
requestHeaders = {
'Accept': 'application/json',
};
}
var reqdata = {
"request_id":requestId.toString(),
"status":requestStatus
};
print('accept request data is $reqdata');
try
{
final response =
await http.post(Connection.url + 'respond-place-request', headers: requestHeaders,body: reqdata);
if (response.statusCode == 200) {
Map<String, dynamic> responseJson = json.decode(response.body);
final existingProductIndex = _items.indexWhere((prod) => prod.id == requestId);
var existingProduct = _items[existingProductIndex];
_items.removeAt(existingProductIndex);
notifyListeners();
return responseJson;
} /*else if (response.statusCode == 500) {
return servererrorresponse;
}*/
} catch (exception) {
throw exception;
}
}
Main Widget class: This is the main widget class where I define Listview widget. I've used provider to get data from api which is written in modal class to populate in Listview and Listview child widgets is in seprate class which is RequestWidgets. In this class I've passed rowitems data to show in listview.
GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
var connectionstatus;
var product;
var _isInit = true;
var _isLoading = false;
#override
void initState() {
super.initState();
}
#override
void didChangeDependencies() {
// TODO: implement didChangeDependencies
if (_isInit) {
setState(() {
_isLoading = true;
});
Provider.of<BandRequestModal>(context).getBandRequestList().then((_) {
setState(() {
_isLoading = false;
});
});
}
_isInit = false;
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
connectionstatus = Provider.of<ConnectivityResult>(context);
product = Provider.of<BandRequestModal>(context, listen: false);
// getRequestData();
return WillPopScope(
onWillPop: _onWillPop,
child: Scaffold(
key: _scaffoldKey,
appBar: CustomAppbar(
_scaffoldKey, Constants.requests, 100.0, filterRecord),
endDrawer: MenuDrawer(),
body:
/*(connectionstatus == ConnectivityResult.wifi ||
connectionstatus == ConnectivityResult.mobile)
? */
Consumer<BandRequestModal>(builder: (context, modal, child) {
return !_isLoading
? Container(child: LayoutBuilder(builder:
(BuildContext context, BoxConstraints constraints) {
return Container(
height: constraints.maxHeight,
child: modal.item.length > 0
? ListView.builder(
padding:
EdgeInsets.only(top: 10.0, bottom: 0.0),
itemCount: modal.item.length,
shrinkWrap: true,
// physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, int i) {
return RequestWidgets(data: modal.item[i]);
})
: Center(
child: new Text(
Constants.norecordfound,
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold),
),
),
// ],
// ),
);
}))
: Comman.loadingIndicator(Theme.of(context).primaryColor);
})
// : Comman.nointernetconnection(context)
// FutureBuilder<BandRequestModal>(
// future: Connection.bandRequestList(),
// builder: (context, snapshot) {
// switch (snapshot.connectionState)
// {
// case ConnectionState.none:
// break;
// case ConnectionState.waiting:
// return Comman.loadingIndicator(
// Theme.of(context).primaryColor);
// break;
// case ConnectionState.active:
// break;
// case ConnectionState.done:
// if (snapshot.hasError) {
// return Center(
// child: new Text(Constants.servererror),
// );
// }else if(snapshot.data==null){
// return Center(
// child: new Text(Constants.servererror),
// );
// } else if (snapshot.data.data.length == 0) {
// return Center(
// child: new Text(
// Constants.norecordfound,
// style: TextStyle(
// fontSize: 20.0, fontWeight: FontWeight.bold),
// ),
// );
// } else {
// return ListView.builder(
// padding:
// EdgeInsets.only(top: 10.0, bottom: 60.0),
// itemCount: snapshot.data.data.length,
// shrinkWrap: true,
// physics: NeverScrollableScrollPhysics(),
// itemBuilder: (context, int i) {
// return RequestWidgets(data:snapshot.data.data[i]);
// });
// }
// break;
// }
// }):Comman.nointernetconnection(context)
));
}
Child widget class: This is the row items class of listview In this class we used many widgets to show place data.
class _RequestWidgetsState extends State<RequestWidgets> {
var getData;
var product;
#override
void initState() {
// TODO: implement initState
getData = widget.data;
super.initState();
}
#override
Widget build(BuildContext context) {
product = Provider.of<BandRequestModal>(context, listen: false);
return Container(
// alignment: Alignment.topLeft,
margin: EdgeInsets.only(top: 5.0),
child: ListTile(
// contentPadding: EdgeInsets.zero,
key: ObjectKey(getData),
leading: CircleAvatar(
radius: 30,
backgroundColor: Colors.transparent,
child: ClipOval(
child: (getData.placeDetails.image != null &&
getData.placeDetails.image != '')
? Image.network(
getData.placeDetails.image,
width: 90,
height: 90,
fit: BoxFit.cover,
)
: Image.asset(
Res.defaultImage,
width: 90,
height: 90,
fit: BoxFit.cover,
)),
),
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
child: Text(getData.placeDetails.name,
style: TextStyle(
fontSize: 16.0,
fontFamily: 'Metropolis',
color: CustomColors.commentTitleColor))),
],
),
subtitle: Container(
margin: EdgeInsets.only(top: 1.0),
child: Column(children: <Widget>[
Container(
margin: EdgeInsets.only(top: 1.0),
child: Row(children: <Widget>[
Expanded(
child: Text(getData.placeDetails.address,
style: TextStyle(
fontSize: 15.0,
height: 1.2,
fontFamily: 'Metropolis',
color: CustomColors.commentSubtitleColor))),
]),
),
Container(
margin: EdgeInsets.only(top: 15.0, bottom: 15.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[],
)),
Divider(
color: CustomColors.commentlineColor,
thickness: 0.8,
)
])),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
GestureDetector(
child: CircleAvatar(
radius: 20,
backgroundColor: Colors.green,
child: Icon(
Icons.check,
color: Colors.white,
),
),
onTap: () {
acceptrejectpopup('1');
// {
// print('accept data $data');
// Comman.hideLoading(context);
// Comman.showSnakBar(data['message'],context);
// });
},
),
SizedBox(
width: 15.0,
),
GestureDetector(
child: CircleAvatar(
backgroundColor: Colors.red,
child: Icon(
Icons.clear,
color: Colors.white,
),
),
onTap: () {
// Comman.showLoading(context);
acceptrejectpopup('0');
/*product.acceptdeclinerequest('0',getData.id.toString()).then((data){
print('decline data $data');
Comman.hideLoading(context);
Comman.showSnakBar(data['message'],context);
});*/
},
)
],
),
),
);
}
//accept and reject
void acceptRejectRequest(String requestStatus) async {
try {
var response =
await product.acceptdeclinerequest(requestStatus, getData.id);
if (response['status'] == Constants.status_true) {
Comman.hideLoading(context);
Comman.showSnakBar(response['message'], context);
// setState(() {});
} else {
Comman.hideLoading(context);
}
} catch (exception) {
Comman.hideLoading(context);
Comman.showSnakBar(Constants.servererror, context);
}
}
//request accept/reject popup
Future<void> acceptrejectpopup(String reqStatus) {
return showDialog(
context: context,
builder: (context) => new AlertDialog(
title: new Text('Alert!',
style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold)),
content: new Text(reqStatus == '1'
? Constants.reqAcceptmessage
: Constants.reqRejectemessage),
actions: <Widget>[
new FlatButton(
onPressed: () => Navigator.of(context).pop(),
child: new Text(Constants.notxt),
),
new FlatButton(
onPressed: () {
Navigator.of(context).pop();
Comman.showLoading(context);
acceptRejectRequest(reqStatus);
},
child: new Text(Constants.yestxt),
),
],
),
);
}
The provider is working just fine, the problem is when the provider notify the consumer the ListView updates the children, but the StatefulWidget check they're the same type (They're all RequestWidget) so they just update themselves (if you don't provide a key to the StatefulWidget they will try to check if they're the same element and update via the didChangeDependencies method), but you're updating the getData var in initState (which will call only once) so even if the consumer updates the value won't. Try it like this
#override
void initState() {
// TODO: implement initState
//getData = widget.data; not here
super.initState();
}
#override
void didChangeDependencies() {
// TODO: implement initState
getData = widget.data; //update it here
super.didChangeDependencies();
}
Other option would be just to give a specific key when building your widget in the itemBuilder so when the consumer updates it changes them accordingly
return RequestWidgets(key: ValueKey(modal.item[i].id),data: modal.item[i]);
// Or some value unique for each item
The problem here I guess is, in your Child widget class, since I can't see any requestId of the selected card being passed to the acceptdeclinerequest().
Your acceptdeclinerequest() expects two unique arguments to be passed when called:
String requestStatus
int requestId
If you look closely into the Child widget class, you are just passing requestStatus. I wonder from where are you getting this getData.id, and how is it identifying that some particular card is selected.
// look here, only requestStatus is being passed
onTap: () {
acceptrejectpopup('0');
}
// and here
onTap: () {
acceptrejectpopup('1');
}
And in your acceptRejectRequest, you are only passing requestStatus
acceptRejectRequest(reqStatus);
And then you call your acceptdeclinerequest() with this data
// machine is confused, where are we getting the getData.id
// it assumes the id as per it's need, hence the error
await product.acceptdeclinerequest(requestStatus, getData.id);
The machine is trying to figure out, which element you selected. Try to give the id from the selected card, and pass it to the method with correct getData.id of that particular element.
Suggestion: Pass in your id of the selected card when you are tapping on it, and then call your methods, and then pass it along to get the right requestId and remove it. Let your methods acceptrejectpopup() and acceptRejectRequest() accept the id of the selected item and then finally pass it to your acceptdeclinerequest()
// first step
onTap: () => acceptrejectpopup('0', your_card_reuqest_id);
// second step, pass data from the above popup method to acceptRejectRequest()
acceptRejectRequest(reqStatus, requestId);
//finally from acceptRejectRequest(reqStatus, requestId), pass it to the final method acceptdeclinerequest
acceptdeclinerequest(requestStatus, requestId);

RangeError (index): Invalid value: Only valid value is 0: 1

I am new to flutter. I am trying to add the list of data to the view. The list of data have different sets of order items with different lengths. I am getting the data from the API but due to the different length of order data, I am getting the error as the image below. I have the json api as follows:
{
"status":"success",
"message":"Data Fetched",
"data":{
"list":[
{
"id":27,
"order_code":"7wfelnkhuodlbvdseley1",
"chef_id":1,
"user_id":1,
"order_status":1,
"status":1,
"order_datetime":"2020-01-21 18:05:00",
"user_location_id":1,
"price":1600,
"coupon_id":null,
"use_coupon":0,
"discount":0,
"final_price":1600,
"vat_amt":208,
"delivery_charge_amt":0,
"payment_id":1,
"delivery_time":null,
"delivery_user_id":null,
"payment_status":0,
"payment_price":null,
"payment_time":null,
"reject_message":null,
"created_at":"2020-01-21 18:05:00",
"updated_at":"2020-01-21 18:07:46",
"orderdata":[
{
"id":30,
"order_code_id":27,
"chef_id":1,
"food_id":17,
"user_id":1,
"additional_info":null,
"food_qty":4,
"instruction":null,
"price":400,
"food":{
"id":17,
"name":"Prawns Chilli",
"description":"<p>Seared prawns smothered in a spicy, sticky Asian sauce,
these Asian Chilli Garlic Prawns will have you smacking your
lips in utter satisfaction, feeling like you’ve just dined at a fancy modern
Thai restaurant.<wbr /> </p>",
"ingredient_detail":null,
"price":500,
"discount_price":400,
"ribbon_text":null,
"image":"1576657695-prawn chilli3.jpg",
"banner_image":null,
"published_date":"2019-12-18",
"is_offer":1
}
}
]
},
{
"id":29,
"order_code":"lzquyrmthdahxmjm81ja1",
"chef_id":1,
"user_id":1,
"order_status":1,
"status":1,
"order_datetime":"2020-01-21 19:17:52",
"user_location_id":1,
"price":280,
"coupon_id":null,
"use_coupon":0,
"discount":0,
"final_price":280,
"vat_amt":36.4,
"delivery_charge_amt":50,
"payment_id":1,
"delivery_time":null,
"delivery_user_id":null,
"payment_status":0,
"payment_price":null,
"payment_time":null,
"reject_message":null,
"created_at":"2020-01-21 19:17:52",
"updated_at":"2020-01-21 19:18:30",
"orderdata":[
{
"id":33,
"order_code_id":29,
"chef_id":1,
"food_id":11,
"user_id":1,
"additional_info":null,
"food_qty":2,
"instruction":null,
"price":250,
"food":{
"id":11,
"name":"Chicken burger",
"description":"<p>The juicyness of the meat will make you feel heaven.</p>",
"ingredient_detail":null,
"price":300,
"discount_price":250,
"ribbon_text":null,
"image":"1576654603-chick burger.jpg",
"banner_image":null,
"published_date":"2019-12-18",
"is_offer":1
}
},
{
"id":34,
"order_code_id":29,
"chef_id":1,
"food_id":4,
"user_id":1,
"additional_info":null,
"food_qty":2,
"instruction":null,
"price":140,
"food":{
"id":4,
"name":"Momo",
"description":"<p>This juicy steamed momos are prepared from the ground water buffalo meat and are called \"Buff momo\". The wrappers are very thinly rolled and the filling is deliciously spicy and juicy, served along with tangy yellow chutney and classic spicy tomato sauce that compliments the taste of steamed momos.</p>",
"ingredient_detail":"<p>Tomato, Ground meat ,Flour, Chilli pepper, Garlic, Ginger, Scallion, Black pepper and Soy sauce</p>",
"price":150,
"discount_price":140,
"ribbon_text":null,
"image":"1576651666-momo.jpg",
"banner_image":null,
"published_date":"2019-11-18",
"is_offer":1
}
}
]
}
]
}
}
My Full Widget Code:
class OnProcessPage extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _OnProcessTile();
}
}
class _OnProcessTile extends State<OnProcessPage> {
bool _isLoading = false;
List<ListD> foodData = [];
List<Orderdata> orderData = [];
SharedPreferences sharedPreferences;
#override
void initState() {
super.initState();
setState(() {
_isLoading = true;
});
getPrefs();
getOnProcessRequest();
}
#override
Widget build(BuildContext context) {
return Center(
child: Stack(
children: <Widget>[
Opacity(
opacity: _isLoading
? 0.3
: 1, // You can reduce this when loading to give different effect
child: AbsorbPointer(
absorbing: _isLoading,
child: _buildCardList(context),
),
),
Opacity(
opacity: _isLoading ? 1.0 : 0,
child: Center(
child: CircularProgressIndicator(
backgroundColor: Theme.of(context).primaryColor,
),
)),
],
));
}
Widget _buildCardList(BuildContext context) {
return ListView.builder(
itemCount: foodData == null ? 0 : foodData.length,
itemBuilder: (context, int index) {
return Wrap(
children: <Widget>[
Container(
margin: EdgeInsets.only(bottom: 10),
child: Card(
child: Column(
children: <Widget>[
_buildCardView(context, index),
_cardBottomView(context, index)
],
)))
],
);
});
}
Widget _buildCardView(BuildContext context, int index) {
return Wrap(
children: <Widget>[
Container(
child: Container(
margin: EdgeInsets.all(10.0),
child: Column(
children: <Widget>[
_cardTopSection(context, index),
_cardMiddleSection(context, index),
_cardTotalPrice(context, index),
Container(
height: 1,
color: Color.fromRGBO(232, 232, 232, 1),
),
],
),
),
),
],
);
}
Widget _cardTotalPrice(BuildContext context, int i) {
return Container(
margin: EdgeInsets.only(bottom: 5.0),
child: Padding(
padding: EdgeInsets.only(top: 3, bottom: 3),
child: Row(
children: <Widget>[
Expanded(
child: Text(""),
),
Expanded(
child: Text("Total",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
)),
),
Expanded(
child: Text(
"${foodData[i].finalPrice}",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
)
],
),
),
);
}
Widget _cardTopSection(BuildContext context, int index) {
return Container(
color: Color.fromRGBO(232, 232, 232, 1),
child: Row(
children: <Widget>[
_topLeftSection(index),
_topmiddleSection(index),
_toprightSection(index)
],
),
);
}
Widget _cardMiddleSection(BuildContext context, int i) {
return Container(
margin: EdgeInsets.only(top: 10.0),
child: ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount:
foodData[i].orderdata == null ? 0 : foodData[i].orderdata.length,
itemBuilder: (context, i) {
print("Item Builder");
print(i);
print("Item Builder");
return _cardMiddleItems(i);
}),
);
}
Widget _cardMiddleItems(int i) {
print("Middle");
print(i);
print("Middle");
return Container(
margin: EdgeInsets.only(bottom: 5.0),
child: Padding(
padding: EdgeInsets.only(top: 3, bottom: 3),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: Text("${orderData[i].food.name}"),
),
Expanded(
child: Text("${orderData[i].foodQty}"),
),
Expanded(
child: Text("${orderData[i].price}"),
),
],
),
),
);
}
Widget _topLeftSection(int index) {
return Container(
child: CircleAvatar(
backgroundImage: AssetImage('assets/images/momo.jpg'),
backgroundColor: Colors.lightGreen,
radius: 24.0,
),
);
}
Widget _topmiddleSection(int i) {
return Expanded(
child: Container(
child: Column(
children: <Widget>[
Text("Coldplay "),
Text("${foodData[i].createdAt}")
// new Text("Hi whatsup?"),
],
),
),
);
}
Widget _toprightSection(int index) {
return Expanded(
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Text(
"#" + "${foodData[index].id}",
style: TextStyle(color: Colors.black, fontSize: 18.0),
),
],
),
),
);
}
Widget _cardBottomView(BuildContext context, int index) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
"Order Status",
style: TextStyle(fontSize: 18),
),
RaisedButton(
onPressed: () => {},
color: Colors.green,
child: Text(
"Cooking",
style: TextStyle(color: Colors.white),
),
),
RaisedButton(
onPressed: () => {
sharedPreferences.setBool('process', true),
sharedPreferences.setBool('new', false),
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => RequestDetails(),
settings: RouteSettings(
arguments: foodData[index],
),
))
},
color: Theme.of(context).primaryColor,
child: Text("Details", style: TextStyle(color: Colors.white)),
),
],
);
}
Future<NewRequestResponse> getOnProcessRequest() async {
print("OnProcess");
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
String token = sharedPreferences.getString("api_token");
Map<String, String> headers = {"Authorization": token};
var jsonResponse;
NewRequestResponse newRequestResponse;
var response = await http.post(
"url",
headers: headers);
if (response.statusCode == 200) {
print("Onprocess Inside 200");
jsonResponse = json.decode(response.body);
print(jsonResponse);
if (jsonResponse != null) {
newRequestResponse = new NewRequestResponse.fromJson(jsonResponse);
print(newRequestResponse);
setState(() {
foodData = newRequestResponse.data.list;
for (int i = 0; i < foodData.length; i++) {
orderData = foodData[i].orderdata;
}
});
setState(() {
_isLoading = false;
});
return newRequestResponse;
} else {
setState(() {
_isLoading = false;
});
return null;
}
} else {
setState(() {
_isLoading = false;
});
jsonResponse = json.decode(response.body.toString());
newRequestResponse = new NewRequestResponse.fromJson(jsonResponse);
print("onProcessRequest outside 200");
return newRequestResponse;
}
}
void getPrefs() async {
sharedPreferences = await SharedPreferences.getInstance();
}
}
It's a little bit tricky, so I hope I can understand the bug, but to me the problem seems to be on your getOnProcessRequest at this line:
for (int i = 0; i < foodData.length; i++) {
orderData = foodData[i].orderdata;
}
you're updating (and overwriting) orderData at each cycle of foodData. I don't think it is the right thing to do.
As much as I understood what should happen is
for each foodData, get the list of orderData.
But foodData is a list, and orderData too. You should, I think, link these items, so that for each foodData you can access to its own orderData list.
EDIT:
I cannot provide a full solution since would take me too much honestly.
But I came up with an example
final Map<String, dynamic> myApi = {
"list": [
{
"id": 1,
"orderdata": [
{"title": "food1"},
{"title": "food2"}
]
},
{
"id": 2,
"orderdata": [
{"title": "food3"},
{"title": "food4"}
]
},
]
};
class OrderData {
final String title;
OrderData(this.title);
#override
String toString() {
return title;
}
}
class FoodData {
final int id;
final List<OrderData> orderData;
FoodData(this.id, this.orderData);
}
void main() {
final tmpMap = (myApi['list'] as List<Map<String, Object>>);
print(tmpMap);
List<FoodData> myList = tmpMap.map<FoodData>((elem) {
final value = elem;
final _id = value['id'] as int;
final List<OrderData> _orderData =
(value['orderdata'] as List<Map<String, String>>)
.map<OrderData>((order) => OrderData(order['title']))
.toList();
return FoodData(_id, _orderData);
}).toList();
print(myList.length);
for (int i = 0; i < myList.length; i++) {
for (int j = 0; j < myList[i].orderData.length; j++) {
print("i: $i, j: $j, elem: ${myList[i].orderData[j]}");
}
}
}
Now at the end what you need is to change your getOnProcessRequest, using a single list, so delete your orderdata, and use only fooddata, and now your foodData would have for each element (food) also a internal rappresentation of the order.
When you want to instanciate the ListView.builder:
For the first (outer ListView) you would use fooddata.length
for the second (inner ListView) you would use foodata[i].orders.length
Hope this would help you with find your right solution