I am learning FLutter and trying out stateful widgets such as sliders, lists, radio buttons and check boxes. I am trying to display a list of girls based on the following parameters:
- Distance
- Age
- Status
- Looking for
When the list is sorted by distance and I click on Is Serious, one of the girls: Monica, appears, when in fact she shouldn't as her looking for is casual.
Similarly, when the list is sorted by age and I click on Is Single, one of the girls: Reshmita appears, when in fact she shouldn't as her status is married.
The following is my code:
girls.dart
class Girls {
String name;
int distance;
int age;
String status;
String lookingFor;
Girls(this.name, this.distance, this.age, this.status, this.lookingFor);
}
girls.screen.dart
import 'package:flutter/material.dart';
import 'package:switch_circle_color/model/girls.dart';
import 'package:switch_circle_color/screens/gender.dart';
import 'package:switch_circle_color/screens/selected_girl_details.dart';
class GirlsScreen extends StatefulWidget {
GirlsScreen({Key? key}) : super(key: key);
@override
State<GirlsScreen> createState() => _GirlsScreenState();
}
class _GirlsScreenState extends State<GirlsScreen> {
int distance = 15;
int age = 25;
String sortCriterion = "distance";
bool isSingle = false;
bool isSerious = false;
void changeDistance(double num) {
setState(() {
distance = num.round();
populateFilteredGirls(distance, age);
//this.filteredGirls = filteredGirls;
});
}
void changeAgeLimit(double a) {
setState(() {
age = a.round();
populateFilteredGirls(distance, age);
});
}
List<Girls> allGirls = [
Girls("Reshmita", 25, 33, "Married", "Serious"),
Girls("Ankita", 17, 26, "Single", "Serious"),
Girls("Rupali", 42, 28, "Single", "Casual"),
Girls("Monica", 50, 24, "Single", "Casual"),
Girls("Sakshi", 9, 27, "Married", "Casual"),
];
List<Girls> filteredGirlsbyDistance = [];
List<Girls> filteredGirlsbyAge = [];
List<Girls> filteredGirls = [];
//List<Girls> filteredGirlsbySerious = [];
//List<Girls> filteredGirlsbySingle = [];
String selectedGirlName = "";
int? selectedGirlDistance;
@override
void initState() {
super.initState();
//filteredGirls = allGirls;
distance = 20;
age = 28;
sortCriterion = "distance";
isSingle = false;
isSerious = false;
(sortCriterion == "distance")
? allGirls.sort((a, b) => a.distance.compareTo(b.distance))
: allGirls.sort((a, b) => a.age.compareTo(b.age));
populateFilteredGirls(distance, age);
}
void populateFilteredGirls(int dis, int ag) {
filteredGirlsbyDistance.clear();
filteredGirlsbyAge.clear();
filteredGirls.clear();
//int len = filteredGirls.length;
for (int i = 0; i < allGirls.length; i ) {
if (allGirls[i].distance <= dis) {
filteredGirlsbyDistance.add(allGirls[i]);
}
}
filteredGirls = filteredGirlsbyDistance;
for (int i = 0; i < filteredGirls.length; i ) {
if (filteredGirls[i].age <= ag) {
filteredGirlsbyAge.add(filteredGirls[i]);
}
}
filteredGirls = filteredGirlsbyAge;
//len = filteredGirls.length;
if (isSingle == true) {
for (int i = 0; i < filteredGirls.length; i ) {
if (filteredGirls[i].status.toLowerCase() != "single") {
filteredGirls.remove(filteredGirls[i]);
}
}
}
if (isSerious == true) {
for (int i = 0; i < filteredGirls.length; i ) {
if (filteredGirls[i].lookingFor.toLowerCase() != "serious") {
filteredGirls.remove(filteredGirls[i]);
}
}
}
//filteredGirls = filteredGirls.toSet().toList();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
Padding(padding: EdgeInsets.all(20)),
// ElevatedButton(
// onPressed: () {
// Navigator.push(context,
// MaterialPageRoute(builder: (context) => GenderScreen()));
// },
// child: Text("Go to Gender Screen")),
// Padding(padding: EdgeInsets.all(30)),
Text("Set max distance"),
Padding(padding: EdgeInsets.all(10)),
Slider(
min: 1.0,
max: 100.0,
divisions: 100,
activeColor: Colors.green,
inactiveColor: Colors.orange,
label: 'Set distance value',
value: distance.toDouble(),
onChanged: (value) {
changeDistance(value);
},
),
Padding(padding: EdgeInsets.all(10)),
Text("Current distance is $distance kms"),
Padding(padding: EdgeInsets.all(10)),
Text("Set max age"),
Padding(padding: EdgeInsets.all(10)),
Slider(
min: 18.0,
max: 60.0,
divisions: 42,
activeColor: Colors.green,
inactiveColor: Colors.orange,
label: 'Set age limit',
value: age.toDouble(),
onChanged: (value) {
changeAgeLimit(value);
},
),
Padding(padding: EdgeInsets.all(10)),
Text("Age limit is $age years"),
Padding(padding: EdgeInsets.all(10)),
Text("Sort by:"),
Padding(padding: EdgeInsets.all(7.5)),
ListTile(
//minLeadingWidth: 30,
title: Text("Age"),
leading: Radio(
value: "age",
groupValue: sortCriterion,
onChanged: (value) {
setState(() {
sortCriterion = value.toString();
allGirls.sort((a, b) => a.age.compareTo(b.age));
populateFilteredGirls(distance, age);
});
},
),
),
Padding(padding: EdgeInsets.all(7.5)),
ListTile(
//minLeadingWidth: 30,
title: Text("Distance"),
leading: Radio(
value: "distance",
groupValue: sortCriterion,
onChanged: (value) {
setState(() {
sortCriterion = value.toString();
allGirls.sort((a, b) => a.distance.compareTo(b.distance));
populateFilteredGirls(distance, age);
});
},
),
),
Padding(padding: EdgeInsets.all(10)),
Text("Is Single?"),
Padding(padding: EdgeInsets.all(2.5)),
InkWell(
onTap: () {
setState(() {
isSingle = !isSingle;
populateFilteredGirls(distance, age);
});
},
child: (isSingle == false)
? Icon(Icons.check_box_outline_blank)
: Icon(Icons.check_box),
),
Padding(padding: EdgeInsets.all(5)),
Text("Is Serious?"),
Padding(padding: EdgeInsets.all(2.5)),
InkWell(
onTap: () {
setState(() {
isSerious = !isSerious;
populateFilteredGirls(distance, age);
});
},
child: (isSerious == false)
? Icon(Icons.check_box_outline_blank)
: Icon(Icons.check_box),
),
Padding(padding: EdgeInsets.all(10)),
Expanded(
child: ListView.builder(
itemCount: filteredGirls.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: const Icon(Icons.girl_outlined),
trailing: Text("${filteredGirls[index].distance} km away"),
title: Text("${filteredGirls[index].name}"),
subtitle: Text(
"${filteredGirls[index].age} years old, ${filteredGirls[index].status}, ${filteredGirls[index].lookingFor}"),
onTap: () {
// setState(() {
// selectedGirlName = filteredGirls[index].name;
// selectedGirlDistance = filteredGirls[index].distance;
// });
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SelectedGirlDetails(
girl: filteredGirls[index],
)));
},
);
},
),
),
//Padding(padding: EdgeInsets.all(25)),
//ElevatedButton(onPressed: () {}, child: Text("Check girl's details")),
// Padding(padding: EdgeInsets.all(10)),
// Text("Selected girl is: $selectedGirlName"),
// Padding(padding: EdgeInsets.all(10)),
// Text("$selectedGirlName is $selectedGirlDistance km away"),
],
),
));
}
}
What is the error with my code and what is the solution?
CodePudding user response:
You can do this with a single query
List<Girls> filteredGirls = allGirls.where((element)=> element.age < 20 && element.distance < 10).toList();
CodePudding user response:
Let's check this loop carefully, Our looping will work i < filteredGirls.length
for (int i = 0; i < filteredGirls.length; i ) {
final x = filteredGirls[i].lookingFor.toLowerCase() != "serious";
if (x) {
filteredGirls.remove(filteredGirls[i]);
}
}
Now for the first-case. We have three girls on filteredGirls.
i => 0;
filteredGirls.length => 3
First girl is Anika and she is serious and doesn't remove list
i => 1;
filteredGirls.length => 3
Now if Rupali is not serious we are removing the girl from filteredGirls.
i => 2;
filteredGirls.length => 2
i < filteredGirls.length breaks here
Now notice our initial list length was 3 and promised to loop on i < filteredGirls.length
. After removing a single item, filteredGirls.length becomes 2. And loop breaks. And Monika stay on filter list.
To handle cases like this, create another list and store removable item, end of the loop remove items. And I prefer using like below and change others filter logic in this pattern.
if (isSerious == true) {
List<Girls> removable = [];
for (final g in filteredGirls) {
if (g.lookingFor.toLowerCase() != "serious") {
removable.add(g);
}
}
for (final g in removable) {
filteredGirls.remove(g);
}
}