Flutter - How to perform multi select in the same list of objects but with a different question? - flutter

I have a list of objects called options
List<Map<String, dynamic>> options = [
{"name": "usually", "value": 100, "id": 1},
{"name": "sometimes", "value": 70, "id": 2},
{"name": "rarely", "value": 40, "id": 3},
{"name": "never", "value": 0, "id": 4},
];
also, I have another list called questions
late List<Map<String, dynamic>> questions = [
{"id": 1, "name": "Q1", "options": options},
{"id": 2, "name": "Q2", "options": options},
{"id": 3, "name": "Q3", "options": options},
{"id": 4, "name": "Q4", "options": options},
{"id": 5, "name": "Q5", "options": options},
{"id": 6, "name": "Q6", "options": options},
{"id": 7, "name": "Q7", "options": options},
{"id": 8, "name": "Q8", "options": options},
{"id": 9, "name": "Q9", "options": options},
{"id": 10, "name": "Q10", "options": options},
{"id": 11, "name": "Q11", "options": options},
{"id": 12, "name": "Q12", "options": options},
{"id": 13, "name": "Q13", "options": options},
{"id": 14, "name": "Q14", "options": options},
];
How can I perform multi select for each question which allow the user select one option per one question ?
Code for the UI
return Scaffold(
body: Column(
children: [
Expanded(
child: ListView.separated(
itemCount: questions.length,
separatorBuilder: (context, index) {
return Divider();
},
itemBuilder: (context, index) {
return ListTile(
title: Text(questions[index]['name']),
subtitle: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: List.generate(
questions[index]['options'].length,
(lowerIndex) => Padding(
padding: const EdgeInsets.all(5.0),
child: InkWell(
onTap: () =>
ChoicesHelper.addValueToSelectedValuesList(
questions[index]['options'][lowerIndex]
['value'],
questions[index]['id'],
questions[index]['options'][lowerIndex]
['id']),
child: Row(
crossAxisAlignment:
CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 20,
height: 20,
color: Colors.grey ),
SizedBox(width: 1),
Text(questions[index]['options']
[lowerIndex]['name'])
])))),
),
);
},
),
),
ElevatedButton(
onPressed: () {
ChoicesHelper.calculateResults();
},
child: Text('Calculate')),
],
),
);
Code for the methods in another class
static List<int> selectedValues = [];
static List<Map<String, dynamic>> answeredQuestionsIds = [];
static void addValueToSelectedValuesList(int value, int questionID, optionID) {
selectedValues.add(value);
answeredQuestionsIds.add({"questionID": questionID,"optionID": optionID});
}
As you can see, I just need to select one box for each question and turn it's color to red, but the problem is that all the row for single question is selected and all the boxes turns into red.
How can I select only one option for one question ?

try this:
class ListWidget extends StatefulWidget {
const ListWidget({Key? key}) : super(key: key);
#override
State<ListWidget> createState() => _ListWidgetState();
}
class _ListWidgetState extends State<ListWidget> {
late List<Map<String, dynamic>> questions = [
{"id": 1, "name": "Q1", "options": options},
{"id": 2, "name": "Q2", "options": options},
{"id": 3, "name": "Q3", "options": options},
{"id": 4, "name": "Q4", "options": options},
{"id": 5, "name": "Q5", "options": options},
{"id": 6, "name": "Q6", "options": options},
{"id": 7, "name": "Q7", "options": options},
{"id": 8, "name": "Q8", "options": options},
{"id": 9, "name": "Q9", "options": options},
{"id": 10, "name": "Q10", "options": options},
{"id": 11, "name": "Q11", "options": options},
{"id": 12, "name": "Q12", "options": options},
{"id": 13, "name": "Q13", "options": options},
{"id": 14, "name": "Q14", "options": options},
];
List<Map<String, dynamic>> options = [
{"name": "usually", "value": 100, "id": 1},
{"name": "sometimes", "value": 70, "id": 2},
{"name": "rarely", "value": 40, "id": 3},
{"name": "never", "value": 0, "id": 4},
];
List<Question> _questions = [];
#override
void initState() {
super.initState();
_questions = questions.map((e) => Question.fromJson(e)).toList();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: _questions.length,
itemBuilder: (context, index) {
return _buildQuestion(
_questions[index],
(int? value) {
print("cvalue = ${value}");
var q = Question(
name: _questions[index].name,
id: _questions[index].id,
options: _questions[index].options,
groupValue: value!);
setState(() {
_questions[index] = q;
});
},
);
}),
);
}
_buildQuestion(Question question, Function(int?)? onChanged) {
return Row(
children: [
Expanded(
child: RadioListTile<int?>(
contentPadding: EdgeInsets.zero,
dense: true,
visualDensity: VisualDensity(horizontal: -4),
title: Text(question.options[0].name),
value: question.options[0].value,
groupValue: question.groupValue,
onChanged: onChanged),
),
Expanded(
child: RadioListTile<int?>(
contentPadding: EdgeInsets.zero,
dense: true,
visualDensity: VisualDensity(horizontal: -4),
title: Text(question.options[1].name),
value: question.options[1].value,
groupValue: question.groupValue,
onChanged: onChanged),
),
Expanded(
child: RadioListTile<int?>(
contentPadding: EdgeInsets.zero,
dense: true,
visualDensity: VisualDensity(horizontal: -4),
title: Text(question.options[2].name),
value: question.options[2].value,
groupValue: question.groupValue,
onChanged: onChanged),
),
Expanded(
child: RadioListTile<int?>(
contentPadding: EdgeInsets.zero,
dense: true,
visualDensity: VisualDensity(horizontal: -4),
title: Text(question.options[3].name),
value: question.options[3].value,
groupValue: question.groupValue,
onChanged: onChanged),
),
],
);
}
}
class Question {
final String name;
final int id;
final List<OptionModel> options;
final int groupValue;
Question(
{required this.name,
required this.id,
required this.options,
required this.groupValue});
static Question fromJson(Map<String, dynamic> json) {
return Question(
name: json['name'],
id: json['id'],
groupValue: -1,
options: (json['options'] as List)
.map((e) => OptionModel.fromJson(e))
.toList());
}
}
class OptionModel {
final String name;
final int value;
final int id;
OptionModel({required this.name, required this.id, required this.value});
static OptionModel fromJson(Map<String, dynamic> json) {
return OptionModel(
name: json['name'], id: json['id'], value: json['value']);
}
}

Related

how to sort data on base of date and avoid data repetition

here is the list which i get from server
`
[
{
"Name": "101 Working hours",
"Balance": "8.00",
"Date": "2022-10-19",
"ShiftName": "AU128"
},
{
"Name": "102 Bonus pay",
"Balance": "3:48",
"Date": "2022-10-19",
"ShiftName": ""
},
{
"Name": "110 Split Shift",
"Balance": "1:00",
"Date": "2022-10-19",
"ShiftName": ""
},
{
"Name": "111 Wage reduction",
"Balance": "1:00",
"Date": "2022-10-19",
"ShiftName": ""
},
{
"Name": "111 Wage reduction",
"Balance": "1:00",
"Date": "2022-10-20",
"ShiftName": ""
},
{
"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": ""
},
]
`
here you can see date is repeating and all i want to avoid date repetition on FE. you can see app Screenshot to get an idea which i get and which i want to achieve.
the data i get.
the data i want to be achieved
I tried to avoid date repetition and insert data on same list if date is same but all the time i get repetition date and data as you can see in image.
my code:
Expanded(
child: 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!),
balance: wageAccountsData[i].balance,
name: wageAccountsData[i].name,
shiftName: wageAccountsData[i].shiftName,
);
},
);
}
return noDataFound(context, 50);
}
return const WageAccountsShimmer();
}),
)
wageacount card
class WageAccountsCard extends StatelessWidget {
final String? date;
final String? balance;
final String? name;
final String? shiftName;
const WageAccountsCard(
{Key? key, this.date, this.name, this.balance,
this.shiftName})
: 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,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
name!,
style: cTextStyle,
),
Text(
balance.toString(),
style: cTextStyle,
),
],
),
SizedBox(
height: Styles.height(context) * 0.01,
),
],
),
);
}
}
model class
class EmployeeWageAccountsModel {
String? name;
String? balance;
String? date;
String? shiftName;
EmployeeWageAccountsModel({this.name, this.balance, this.date,
this.shiftName});
EmployeeWageAccountsModel.fromJson(Map<String, dynamic> json) {
name = json['Name'];
balance = json['Balance'];
date = json['Date'];
shiftName = json['ShiftName'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['Name'] = name;
data['Balance'] = balance;
data['Date'] = date;
data['ShiftName'] = shiftName;
return data;
}
}
parsing data from API
List<EmployeeWageAccountsModel> employeeWageAccountsList =
List<EmployeeWageAccountsModel>.from(
employeeWageAccountsResponse.map((model) =>
EmployeeWageAccountsModel.fromJson(model)));
You can use collection package and group your data with date :
var grouped = groupBy(xdata, (Map value) => value['Date']);
then you can make list base on grouped. Note that xdata is your list that you get from server.
grouped is map and contain this:
{
2022-10-19: [
{Name: 101 Working hours, Balance: 8.00, Date: 2022-10-19, ShiftName: AU128},
{Name: 102 Bonus pay, Balance: 3:48, Date: 2022-10-19, ShiftName: },
{Name: 110 Split Shift, Balance: 1:00, Date: 2022-10-19, ShiftName: },
{Name: 111 Wage reduction, Balance: 1:00, Date: 2022-10-19, ShiftName: }
],
2022-10-20: [
{Name: 111 Wage reduction, Balance: 1:00, Date: 2022-10-20, ShiftName: }
],
2022-10-21: [
{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: }
]
}
also change your model class to this:
class EmployeeWageAccountsModel {
String? date;
String? shiftName;
List<Wage>? wages;
EmployeeWageAccountsModel({this.wages, this.date, this.shiftName});
EmployeeWageAccountsModel.fromJson(Map<String, dynamic> json) {
wages = (json.values.first as List).map((e) => Wage.fromJson(e)).toList();
date = json.keys.first;
shiftName = json.values.first[0]['ShiftName'];
}
...
}
class Wage {
String? name;
String? balance;
Wage({this.name, this.balance});
Wage.fromJson(Map<String, dynamic> json) {
name = json['Name'];
balance = json['Balance'];
}
}
and parse it like this:
var grouped =
groupBy(employeeWageAccountsResponse, (Map value) => value['Date']);
List<EmployeeWageAccountsModel> employeeWageAccountsList =
List<EmployeeWageAccountsModel>.from(grouped.entries
.map((e) => EmployeeWageAccountsModel.fromJson({e.key: e.value})));
then change your list item to this:
class WageAccountsCard extends StatelessWidget {
final String? date;
final String? shiftName;
final List<Wage>? 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,
),
ListView.builder(
itemBuilder: (context, index) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
wages![index].name!,
style: cTextStyle,
),
Text(
wages![index].balance.toString(),
style: cTextStyle,
),
],
);
},
itemCount: wages!.length,
shrinkWrap: true,
),
SizedBox(
height: Styles.height(context) * 0.01,
),
],
),
);
}
}
You could build a Map<String, List<Map<String, String>>> using forEach where key would be unique dates and values would be the list of objects.
void main() {
const data = [
{
"Name": "101 Working hours",
"Balance": "8.00",
"Date": "2022-10-19",
"ShiftName": "AU128"
},
{
"Name": "102 Bonus pay",
"Balance": "3:48",
"Date": "2022-10-19",
"ShiftName": ""
},
{
"Name": "110 Split Shift",
"Balance": "1:00",
"Date": "2022-10-19",
"ShiftName": ""
},
{
"Name": "111 Wage reduction",
"Balance": "1:00",
"Date": "2022-10-19",
"ShiftName": ""
},
{
"Name": "111 Wage reduction",
"Balance": "1:00",
"Date": "2022-10-20",
"ShiftName": ""
},
{
"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": ""
},
];
final Map<String, List<Map<String, String>>> dates = {};
data.forEach((e) {
if (dates.containsKey(e['Date'])) {
dates[e['Date']]?.add(e);
} else {
dates[e['Date']!] = [e];
}
});
dates.forEach((e, k) {
print("$e\n${k.join("\n")}");
print('\n');
});
}
One way to achive this is to use equatable package so you can compare objects
class Person extends Equatable {
const Person(this.name,this.date);
final String name;
final DateTime date;
#override
List<Object> get props => [name,date];
}
now you can compare two objects of type Person and with a simple function you can remove the repetition .
example:
List<Person> yourList = [];
// before you add new object check if its already available
int getEqual(Person onePerson)=>yourList.indexWhere((element)=> element == onePerson);
if (getEqual(onePerson) != -1){
// object was not found in the list
yourList.add(onePerson);
}
// then sort your list by date using sort method
yourList.sort((a,b) => a.date.compareTo(b.date));

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(() {});
},
),
),
);
},
)
],
),
);
},
),
);
}
}

Dart: How to Calculate two values in ListviewBuilder using TextFromFields?

How to Calculate values inlistviewbuilder?
> like 15+23=38
etc...
Using this list in List View builder How to calculate Values?
var product = [
{"id": 1, "items": "Item 1", "qty": 15},
{"id": 2, "items": "Item 2", "qty": 20},
{"id": 3, "items": "Item 3", "qty": 50},
{"id": 4, "items": "Item 4", "qty": 12},
{"id": 5, "items": "Item 5", "qty": 35},
{"id": 6, "items": "Item 6", "qty": 26},
{"id": 7, "items": "Item 7", "qty": 12},
{"id": 8, "items": "Item 8", "qty": 25},
{"id": 9, "items": "Item 9", "qty": 22},
{"id": 10, "items": "Item 10", "qty": 28},
{"id": 11, "items": "Item 11", "qty": 24},
{"id": 12, "items": "Item 12", "qty": 16}
];
how to calculate in Listviewbuilder??
We need List<TextEditingController> with listen for updating value.
You can follow this widget and decorate the way you like
class ListVCalculator extends StatefulWidget {
const ListVCalculator({Key? key}) : super(key: key);
#override
State<ListVCalculator> createState() => _ListVCalculatorState();
}
class _ListVCalculatorState extends State<ListVCalculator> {
final int itemsLength = 12;
List<TextEditingController> controllers = [];
List<int> firstNumbers = [];
#override
void initState() {
super.initState();
controllers = List.generate(itemsLength, (index) {
firstNumbers.add(index + 1 * index + 20); // use random number
return TextEditingController()
..addListener(() {
setState(() {});
});
});
}
String _result(int index) {
final int num =
int.tryParse(controllers[index].text.trim().toString()) ?? 0;
return "${num + firstNumbers[index]}";
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: itemsLength,
itemBuilder: (context, index) {
return ListTile(
leading: Text("Item $index"),
title: Row(
children: [
Text("${firstNumbers[index]}"),
const SizedBox(
width: 100,
),
SizedBox(
width: 100,
child: TextField(
controller: controllers[index],
),
)
],
),
trailing: Text(_result(index)),
);
},
),
);
}
}

List inside list with checkbox in 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'),
],
),
),
],
);

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']));