List inside list with checkbox in flutter - flutter

I need to create a checkbox with select all functionality , here is my sample json
[
{
"id": 1,
"name": "categories 1",
"child": [
{
"id": 29,
"name": "sub 1"
},
{
"id": 30,
"name": "sub 2"
},
{
"id": 31,
"name": "sub 3 "
}
]
},
{
"id": 2,
"name": "categories 2",
"child": [
{
"id": 23,
"name": "sub 1"
},
{
"id": 24,
"name": "sub 1"
},
{
"id": 25,
"name": "sub 1"
}
]
},
{
"id": 3,
"name": "categories 3",
"child": [
{
"id": 222,
"name": "sub 1"
},
{
"id": 229,
"name": "sub 1"
},
{
"id": 229,
"name": "sub 1"
}
]
}
]
How i need is
If I check categories 1 all under categories should be selected/unselected and if I select category 2 all under that should be selected and also individual item also need to be selected
Select all should select all checkbox in all categories

Try something like this.
Container(
child: ListView.builder(
itemCount: 50,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Wrap(
children: [
Container(
child: Icon(
Icons.check_box,
)
),
Container(
child: Text(
'Category ${index+1}'
),
),
],
),
Container(
margin: EdgeInsets.only(left: 16),
child: ListView.builder(
itemCount: 3,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index2) {
return Wrap(
children: [
Container(
child: Icon(
Icons.check_box,
)
),
Container(
child: Text(
'Subcategory ${index2+1}'
),
),
],
);
}
)
)
]
)
);
}
)
)
Then you have to put your own logic according to your requirement.

I think using this is more flexible, tree_view:
var treeView = TreeView(
parentList: [
Parent(
parent: Text('Desktop'),
childList: ChildList(
children: <Widget>[
Parent(
parent: Text('documents'),
childList: ChildList(
children: <Widget>[
Text('Resume.docx'),
Text('Billing-Info.docx'),
],
),
),
Text('MeetingReport.xls'),
Text('MeetingReport.pdf'),
Text('Demo.zip'),
],
),
),
],
);

Related

how to check value in list empty or not and if not get the value

I am getting a list from servers like this
[
{
"Date": "2022-10-21",
"Wages": [
{
"Name": "101 Working hours",
"Balance": "8.00",
"Date": "2022-10-21",
"ShiftName": "AU128"
},
{
"Name": "102 Bonus pay",
"Balance": "3:48",
"Date": "2022-10-21",
"ShiftName": ""
},
{
"Name": "110 Split Shift",
"Balance": "1:00",
"Date": "2022-10-21",
"ShiftName": ""
},
{
"Name": "111 Wage reduction",
"Balance": "1:00",
"Date": "2022-10-21",
"ShiftName": ""
}
]
},
]
I want to get ShiftName if it is not empty and shows at FE. shift name is in Wages List
Text(
Wages[i].shiftName ?? "",
style: wageTextStyle,
),
I try to use List.any((any) => any.containsValue());
but I do not know which value I get from server because shiftName can be changed
my API calling method from provider Consumer
Consumer<EmployeeWageAccountsProvider>(
builder: (context, data, child) {
if (!data.isLoading) {
int length = data.getEmployeeAccountsData!.length;
if (data.getEmployeeAccountsData!.isNotEmpty) {
wageAccountsData = data.getEmployeeAccountsData!;
return ListView.builder(
itemCount: length,
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemBuilder: (context, i) {
return WageAccountsCard(
date: Helper.formatStringDate(
wageAccountsData[i].date!),
shiftName: wageAccountsData[i].wages!
[i].shiftName!.isEmpty ? "":wageAccountsData[i].wages!
[i].shiftName,
wages: wageAccountsData[i].wages,
);
},
);
}
return noDataFound(context, 50);
}
return const WageAccountsShimmer();
}),
wage accounts card to displaying data to user
class WageAccountsCard extends StatelessWidget {
final String? date;
final String? shiftName;
final List<Wages>? wages;
const WageAccountsCard(
{Key? key,
this.date,
this.shiftName,
this.wages})
: super(key: key);
#override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(10),
padding: const EdgeInsets.all(8),
decoration: CustomBoxDecoration.cardDecoration(context,
shadow: true),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
shiftName ?? "",
style: wageTextStyle,
),
Text(
date.toString(),
style: wageTextStyle,
),
],
),
SizedBox(
height: Styles.height(context) * 0.01,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
S.of(context).wage_type,
style: cTextStyle,
),
Text(
S.of(context).balance,
style: cTextStyle,
),
],
),
const Divider(
color: Colors.black,
),
]
),
);
}
}
shiftName ?? "", used to return default value on null case.
You are getting empty String, therefore you can do
shiftName.isEmpty ? "onEmptyCaseValue" : shiftName
return shiftName.isEmpty ? Center(child: Text('Empty')) : Text(
shiftName ?? "",
style: wageTextStyle,
),
wageAccountsData[i].wages!
[i].shiftName!.isEmpty ? "":wageAccountsData[i].wages[i].shiftName,
insted of this just write like this
wageAccountsData[i].wages![i].shiftName ?? "Default Text",

How to select one checkbox in listView inside listview in Flutter

how to select one checkbox in each answer for each question. so that each question can only be chosen one answer. this is my data and this is my code
This is data example:
List<dynamic> survey = [
{
"id": 1,
"question":"Question1",
"answer": [
{"id": 1, "name": "Option A"},
{"id": 2, "name": "Option B"},
{"id": 3, "name": "Option C"},
{"id": 4, "name": "Option D"}
],
},
{
"id": 2,
"question":
"Question2",
"answer": [
{"id": 1, "name": "Option A"},
{"id": 2, "name": "Option B"},
{"id": 3, "name": "Option C"},
{"id": 4, "name": "Option D"}
],
},
];
This is my code:
bool isSelected = false;
onSelected(value) {
setState(() {
isSelected = value!;
});
}
#override
Widget build(BuildContext context) {
return ListView.builder(
shrinkWrap: true,
itemCount: survey.length,
itemBuilder: (BuildContext context, int index) {
var number = index + 1;
var listAnswer = survey[index]["answer"];
return Container(
padding: EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.9,
child: Text(
survey[index]["question"],
maxLines: 5,
overflow: TextOverflow.ellipsis,
),
),
SizedBox(height: 10),
ListView.builder(
shrinkWrap: true,
itemCount: listAnswer.length,
itemBuilder: (BuildContext context, int index) {
return Container(
margin: EdgeInsets.only(bottom: 10),
child: ListTile(
title: Text(listAnswer[index]["name"]),
trailing: Checkbox(
checkColor: Colors.white,
value: isSelected,
shape: CircleBorder(),
onChanged: (bool? value) {
onSelected(value);
},
),
),
);
},
)
],
),
);
},
);
}
It would be easier with model class. I am using a map to store the selected answer.
Map<String, int> selectedAnswer = {};
Method checking whether current answer is selected or not.
bool isSelected(String qustion, int answerID) {
if (selectedAnswer.containsKey(qustion) &&
selectedAnswer[qustion] == answerID) {
return true;
}
return false;
}
And switching value
onChanged: (bool? value) {
if (_isSelected) {
selectedAnswer[question] =
listAnswer[index]["id"];
} else {
selectedAnswer
.addAll({question: listAnswer[index]["id"]});
}
setState(() {});
},
Full widget.
class TEL extends StatefulWidget {
const TEL({super.key});
#override
State<TEL> createState() => _TELState();
}
class _TELState extends State<TEL> {
List<dynamic> survey = [
{
"id": 1,
"question": "Question1",
"answer": [
{"id": 1, "name": "Option A"},
{"id": 2, "name": "Option B"},
{"id": 3, "name": "Option C"},
{"id": 4, "name": "Option D"}
],
},
{
"id": 2,
"question": "Question2",
"answer": [
{"id": 1, "name": "Option A"},
{"id": 2, "name": "Option B"},
{"id": 3, "name": "Option C"},
{"id": 4, "name": "Option D"}
],
},
];
Map<String, int> selectedAnswer = {};
bool isSelected(String qustion, int answerID) {
if (selectedAnswer.containsKey(qustion) &&
selectedAnswer[qustion] == answerID) {
return true;
}
return false;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(8),
itemCount: survey.length,
itemBuilder: (BuildContext context, int index) {
var number = index + 1;
final question = survey[index]["question"];
List<Map> listAnswer = survey[index]["answer"];
return Container(
padding: EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.9,
child: Text(
question,
maxLines: 5,
overflow: TextOverflow.ellipsis,
),
),
SizedBox(height: 10),
ListView.builder(
shrinkWrap: true,
itemCount: listAnswer.length,
itemBuilder: (BuildContext context, int index) {
final bool _isSelected =
isSelected(question, listAnswer[index]["id"]);
return Container(
margin: EdgeInsets.only(bottom: 10),
child: ListTile(
title: Text(listAnswer[index]["name"].toString()),
trailing: Checkbox(
checkColor: Colors.white,
value: _isSelected,
shape: CircleBorder(),
onChanged: (bool? value) {
if (_isSelected) {
selectedAnswer[question] =
listAnswer[index]["id"];
} else {
selectedAnswer
.addAll({question: listAnswer[index]["id"]});
}
setState(() {});
},
),
),
);
},
)
],
),
);
},
),
);
}
}

How to select only one radio button for each object array in Flutter

How to choose only one radio button for each question. now, in my code there is a problem when choosing the answer "option A" in the first question, then the answer "option X" in the second question is also selected. I don't want that to happen, I want when only selecting the answer from the first question, then only the radio button in the first question is selected.
this is my data dummy:
List<dynamic> dataDummy = [
{
"id": 1,
"question": "Lorem ipsum dolor sit amet",
"option": [
{"id": 1, "name": "Option A"},
{"id": 2, "name": "Option B"},
],
},
{
"id": 2,
"question": "consectetur adipiscing elit",
"option": [
{"id": 1, "name": "Option X"},
{"id": 2, "name": "Option Y"},
],
},
];
this is my code:
int selectedRadio = 0;
setSelectedRadio(int val) {
setState(() {
selectedRadio = val;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(8),
itemCount: dataDummy.length,
itemBuilder: (BuildContext context, int index) {
var number = index + 1;
final optionAnswer = dataDummy[index]["option"];
return Container(
padding: EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 20),
SizedBox(
width: MediaQuery.of(context).size.width * 0.9,
child: Text(
dataDummy[index]["question"],
maxLines: 5,
overflow: TextOverflow.ellipsis,
),
),
SizedBox(height: 10),
ListView.builder(
shrinkWrap: true,
itemCount: optionAnswer.length,
itemBuilder: (BuildContext context, int index) {
return Container(
margin: EdgeInsets.only(bottom: 10),
child: ListTile(
title: Text(optionAnswer[index]["name"]),
trailing: Radio<dynamic>(
value: optionAnswer[index]["id"],
groupValue: selectedRadio,
onChanged: (val) {
setSelectedRadio(val);
},
),
),
);
},
)
],
),
);
},
),
);
}
this is the result code:
Because you have the same id in both of your questions.
You have to change the ids or use this optionAnswer[index]["name"] instead of optionAnswer[index]["id"]
value parameter on the radio is what makes it unique, in your case you have same values because optionAnswer[index]["id"] are equal, consider alway making id something unique f.e. use UniqueKey()
Use a map to store selected answer.
Map<String, int> selectedAnswer = {};
And changes will be
trailing: Radio<int?>(
value: optionAnswer[index]["id"],
groupValue: selectedAnswer[question],
onChanged: (val) {
selectedAnswer[question] = val ?? -1; //it wont trigger mostly, feels better than using !
setState(() {});
},
),
Test snippet
class RDTest extends StatefulWidget {
const RDTest({super.key});
#override
State<RDTest> createState() => _RDTestState();
}
class _RDTestState extends State<RDTest> {
List<dynamic> dataDummy = [
{
"id": 1,
"question": "Lorem ipsum dolor sit amet",
"option": [
{"id": 1, "name": "Option A"},
{"id": 2, "name": "Option B"},
],
},
{
"id": 2,
"question": "consectetur adipiscing elit",
"option": [
{"id": 1, "name": "Option X"},
{"id": 2, "name": "Option Y"},
],
},
];
Map<String, int> selectedAnswer = {};
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(8),
itemCount: dataDummy.length,
itemBuilder: (BuildContext context, int index) {
final List optionAnswer = dataDummy[index]["option"];
final question = dataDummy[index]["question"];
return Container(
padding: EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 20),
SizedBox(
width: MediaQuery.of(context).size.width * 0.9,
child: Text(
question,
maxLines: 5,
overflow: TextOverflow.ellipsis,
),
),
SizedBox(height: 10),
ListView.builder(
shrinkWrap: true,
itemCount: optionAnswer.length,
itemBuilder: (BuildContext context, int index) {
return Container(
margin: EdgeInsets.only(bottom: 10),
child: ListTile(
title: Text(optionAnswer[index]["name"]),
trailing: Radio<int?>(
value: optionAnswer[index]["id"],
groupValue: selectedAnswer[question],
onChanged: (val) {
selectedAnswer[question] =
val ?? 0; //it wont trigger mostly
setState(() {});
},
),
),
);
},
)
],
),
);
},
),
);
}
}

Filter Items In GridView With Tab Bar

I have a Listview that contains a Gridview and I'm trying to filter the results based on the list headers in the list view but can't seems to make it work.
When I tap a tab button, I want the grid view to only show products that has the matching attribute. This is what my app looks like for reference:
EDIT 1: _changedDropDownItem
void _changedDropDownItem(int newValue) {
setState(() {
_selectedCustomerType = newValue;
});
makeDropdown(newValue);
print(_selectedCustomerType);
}
void makeDropdown(filterNumb) {
if (filterNumb == 0) {
_dropdownList = _all;
} else if (filterNumb == 1) {
_dropdownList = _feeling;
} else if (filterNumb == 2) {
_dropdownList = _productType;
} else if (filterNumb == 3) {
_dropdownList = _filter;
}
// _sortTabs(filterNumb, _filterCat, _productTabFilter, _dropdownList);
print(_dropdownList);
}
The Headers for each tab are as followed:
List _all = ['All'];
List _feeling = [
'Creativity',
'Energetic',
'Euphoric',
'Focused',
'Giggly',
'Happy',
'Hungry',
'Relaxed',
'Sleepy',
'Sexy Time',
'Talkative',
'Tingly',
'Uplifted',
];
List _productType = [
'Flower',
'Concentrates',
'Dabs',
'For Pets',
'Storage',
'Topical',
'Vaping',
'Home Setup',
'Edibles',
'Pre-Rolled',
'Bongs & Pipes',
];
List _usage = [
'Headache',
'Insomnia',
'Being Social',
'First Timers',
'Cramps\n & Pains',
'Body Pains',
'Stress and\n Tension',
'Creativity',
'Productivity',
'Accessory',
];
List _filter = [
'Headache',
'Insomnia',
'Being Social',
'First Timers',
'Cramps\n & Pains',
'Body Pains',
'Stress and\n Tension',
'Creativity',
'Productivity',
'Accessory',
];
Creating The ListView
Container(
child: ListView.builder(
scrollDirection: Axis.vertical,
itemCount: _dropdownList.length,
shrinkWrap: true,
itemBuilder: (context, _tabIndex) {
return Container(child: _buildProduct(context, _tabIndex));
},
Edit 2: JSON Data:
{
"product": [
{
"id": 28,
"name": "Blueberry Waltz",
"short_description": "Blue Frost is a hybrid that balances the genetics of Blue Monster and Jack Frost to create a 60/40 indica-dominant cross. Breeder Goldenseed has developed this strain to produce dense buds that show a range of deep violet hues and produces a pungent mixture of aromas. The flavor is an interesting combination of sweet fruity notes with a sharp cheese-like undertone. This hybrid is sure to lift your mood and replace any stress you may have with smile on your face.",
"usage": "First Timers",
"effect": "Energetic",
"quantity": 20,
"sizes": [
{
"id": 57,
"size": 3,
"product_id": 28,
"price": 25.0
},
{
"id": 58,
"size": 4,
"product_id": 28,
"price": 50.0
}
],
"image": "https://dispensaries.s3.amazonaws.com/product_images/grams.png",
"image2": "https://dispensaries.s3.amazonaws.com/product_images/blueberry-waltz-1.jpg",
"image3": "https://dispensaries.s3.amazonaws.com/product_images/GRAND-DADDY-PURPLE.jpg",
"Product_Type": "Carry Case",
"Product_Category": "Pets",
"thc_content": 92.0,
"cbd_content": 0.0
},
{
"id": 27,
"name": "Pink Champagne",
"short_description": "Wrawer",
"usage": "First Timers",
"effect": "Creative",
"quantity": 9,
"sizes": [
{
"id": 56,
"size": 2,
"product_id": 27,
"price": 29.99
}
],
"image": "https://dispensaries.s3.amazonaws.com/product_images/char.png",
"image2": "https://dispensaries.s3.amazonaws.com/product_images/Pink_Champagne_A3pza9w.jpeg",
"image3": "https://dispensaries.s3.amazonaws.com/product_images/Pink_Champagne_V65X8hn.jpeg",
"Product_Type": "Cartridge",
"Product_Category": "Storage",
"thc_content": 23.2,
"cbd_content": 20.0
}
{
"id": 29,
"name": "test prod",
"short_description": "wassup",
"usage": "First Timers",
"effect": "Euphoric",
"quantity": 4,
"sizes": [
{
"id": 59,
"size": 3,
"product_id": 29,
"price": 29.99
}
],
"image": "https://dispensaries.s3.amazonaws.com/product_images/1_1.png",
"image2": "https://dispensaries.s3.amazonaws.com/product_images/2_1.png",
"image3": "https://dispensaries.s3.amazonaws.com/product_images/2.png",
"Product_Type": "indica",
"Product_Category": "Dabs",
"thc_content": 20.0,
"cbd_content": 0.0
}
]
}
Create the Product GridView with data
Widget _buildProduct(BuildContext context, _tabIndex) {
return Container(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(children: <Widget>[
Padding(
padding: const EdgeInsets.only(left: 8.0, top: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
_dropdownList[_tabIndex],
style: TextStyle(
fontFamily: 'Poppins',
color: buddiesPurple,
fontSize: 18),
),
],
),
),
SizedBox(
height: screenAwareSize(10, context),
),
Container(
// padding: EdgeInsets.only(top: 10.0),
// height: 350,
width: MediaQuery.of(context).size.width,
child: FutureBuilder(
future: _setProducts,
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(
child: CircularProgressIndicator(),
);
break;
case ConnectionState.none:
break;
case ConnectionState.active:
break;
case ConnectionState.done:
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
else
productList = snapshot.data;
_productTabFilter = snapshot.data;
break;
}
return GridView.builder(
itemCount: _productTabFilter.length,
controller: ScrollController(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 20,
childAspectRatio: 0.7),
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemBuilder: (context, i) {
return InkWell(
onTap: () {
currentRes = restaurant;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SelectedProduct(
restaurant: restaurant,
product: _productTabFilter[i],
)));
},
child: Stack(children: <Widget>[
_buildProductList(
_productTabFilter[i].name,
_productTabFilter[i].image2,
_productTabFilter[i].usage,
_productTabFilter[i].effect,
_productTabFilter[i].productType,
'${_productTabFilter[i].sizes[0].price.toStringAsFixed(2)}',
_productTabFilter[i].quantity)
]),
);
},
);
},
))
])));
}
This is my drop down that will contain the header names:
//Drop down
String _filterDropdown;
List _dropdownList = [];
List<DropdownMenuItem<String>> _dropDownMenuItems;
List<DropdownMenuItem<String>> buildAndGetDropDownMenuItems(List tempList) {
List<DropdownMenuItem<String>> items = List();
for (String x in tempList) {
items.add(DropdownMenuItem(
value: x,
child: Text(
x,
style: TextStyle(
fontSize: 14,
fontFamily: 'Roboto-Regular',
fontWeight: FontWeight.w700,
color: bPurple),
)));
}
return items;
}
And the tab bar that when tapped changes all of the headers to the next category
TabBar(
onTap: _changedDropDownItem,
indicatorColor: bGreen,
labelColor: bPurple,
unselectedLabelColor: Colors.grey,
// isScrollable: true,
tabs: [
Tab(
icon: Icon(FontAwesomeIcons.medkit),
text: "Recovery"),
Tab(icon: Icon(FontAwesomeIcons.heart), text: "Feels"),
Tab(icon: Icon(Icons.info), text: "Product"),
Tab(
icon: Icon(FontAwesomeIcons.home),
text: "More",
),
],
),
Please try this first:
void _changedDropDownItem(int newValue) {
setState(() {
makeDropdown(newValue);
_selectedCustomerType = newValue;
});
// makeDropdown(newValue);
print(_selectedCustomerType);
}

flutter how to update grid view

i have grid view shows all data in list and i have drop down with values like price from low to high and when user select any value then i will re-sort the list at this point all is work correctly and list updated but grid view stell with old list.
i cannot update grid view
class ProductsListState extends State<ProductsList> {
double width;
String _title;
String _selectedSort;
String _language;
ProductsListState(this._title);
List productsList;
int filter;
#override
Widget build(BuildContext context) {
MediaQueryData deviceInfo = MediaQuery.of(context);
width = deviceInfo.size.width;
_language = _language ?? AppLocalizations.of(context).tr('lang');
filter = filter ?? 1;
_selectedSort ??= getSortFilter(_language)[0];
productsList = (productsList ?? getProductsList(filter));
print(productsList);
print(filter);
var size = MediaQuery.of(context).size;
var data = EasyLocalizationProvider.of(context).data;
return EasyLocalizationProvider(
data: data,
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
backgroundColor: Color.fromRGBO(205, 14, 64, 1),
appBar:
MarketinoAppBar(title: _title, width: width, appContext: context),
body: Stack(
children: <Widget>[
Container(
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
blurRadius: 13.0,
color: Colors.black.withOpacity(.5),
offset: Offset(6.0, 7.0),
),
],
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20)),
),
),
Column(
children: <Widget>[
Padding(
padding: EdgeInsetsDirectional.fromSTEB(0, 0, 20, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text('SORT BY: '),
DropdownButton<String>(
value: _selectedSort,
onChanged: (value) {
setState(() {
productsList.sort(
(a, b) => a['price'].compareTo(b['price']));
_selectedSort = value;
filter = 2;
});
},
items: getSortFilter(_language).map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
],
),
),
Container(
child: filter == 1
? Grid(productsList, width, size)
: Grid(getProductsList(2), width, size),
)
],
)
],
),
),
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
EasylocaLizationDelegate(locale: data.locale, path: "assets/language")
],
supportedLocales: [Locale('en', 'US'), Locale('ar', 'SA')],
locale: data.savedLocale,
),
);
}
//replace by json
List getProductsList(int filter) {
List list = [];
list.add({
"id": "1",
"name": "sssss",
"imageUrl": "assets/images/Frozen Food.jpg",
"description": "qwerfghjhgfdsaasdfghnjmhgfdsa",
"price": "110",
"quantity": "1",
"isLiked": "false",
"max": "5"
});
list.add({
"id": "2",
"name": "sssss",
"imageUrl": "assets/images/Frozen Food.jpg",
"description": "qwerfghjhgfdsaasdfghnjmhgfdsa",
"price": "112",
"quantity": "1",
"isLiked": "true",
"max": "5"
});
list.add({
"id": "3",
"name": "sssss",
"imageUrl": "assets/images/Frozen Food.jpg",
"description": "qwerfghjhgfdsaasdfghnjmhgfdsa",
"price": "114",
"quantity": "1",
"isLiked": "true",
"max": "10"
});
list.add({
"id": "4",
"name": "sssssssssssssssssssssssssssssssssssssssssssssssssssssssszzzzz",
"imageUrl": "assets/images/Frozen Food.jpg",
"description": "qwerfghjhgfdsaasdfghnjmhgfdsa",
"price": "11",
"quantity": "1",
"isLiked": "false",
"max": "1"
});
list.add({
"id": "5",
"name": "sssss",
"imageUrl": "assets/images/Frozen Food.jpg",
"description": "qwerfghjhgfdsaasdfghnjmhgfdsa",
"price": "110",
"quantity": "1",
"isLiked": "false",
"max": "15"
});
switch (filter) {
case 1:
list.sort((a, b) => a['id'].compareTo(b['id']));
break;
case 2:
list.sort((a, b) => a['price'].compareTo(b['price']));
break;
}
return list;
}
List<String> getSortFilter(String language) {
if (language == 'english')
return [
"الأكثر رواجا",
"السعر من الأقل للأعلى",
"السعر من الأعلى للأقل",
"التخفيضات",
];
else
return [
"Popularty",
"Price: Low to High",
"Price: High to Low",
"Discount",
];
}
}
class Grid extends StatefulWidget {
List productsList;
var _width;
var _size;
Grid(this.productsList, this._width, this._size);
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return GridState(productsList, _width, _size);
}
}
class GridState extends State<Grid> {
List productsList;
var _width;
var _size;
GridState(this.productsList, this._width, this._size);
#override
Widget build(BuildContext context) {
// TODO: implement build
return Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: new GridView.builder(
itemCount: productsList.length,
gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: _width > 600 ? 3 : 2,
childAspectRatio: _width > 600 ? 0.6 : _width > 400 ? .725 : .685,
// childAspectRatio: (_itemWidth / _itemHeight),
),
itemBuilder: (BuildContext context, int index) {
return ProductGridTile(
price: double.parse(productsList[index]['price']),
width: _size.width,
image: productsList[index]['imageUrl'],
title: productsList[index]['name'],
likeStatus: productsList[index]['isLiked'] == 'true',
max: double.parse(productsList[index]['max']),
id: int.parse(productsList[index]['id']));
}),
),
);
}
}
gridview updated after re-sort list
You are doing some operations inside the build() method, when you call setState() these operations would be called again. Check that part and also try to update the list this way:
productsList = List.from(productsList)..sort(
(a, b) => a['price'].compareTo(b['price']));