Issue
I have filters that filter the user by city and country. You can select multiple cities or/and countries at the same time. The list can be hidden and opened (By default it is closed). It looks like this
It matches the following code
class FilterDialogUser extends StatefulWidget {
final void Function(Map<String, List<String>?>) onApplyFilters;
final Map<String, List<String>?> initialState;
const FilterDialogUser({
Key? key,
required this.onApplyFilters,
this.initialState = const {},
}) : super(key: key);
@override
State<FilterDialogUser> createState() => _FilterDialogUserState();
}
class _FilterDialogUserState extends State<FilterDialogUser> {
// Temporary storage of filters.
Map<String, List<String>?> filters = {};
bool needRefresh = false;
// Variable for the ability to hide all elements of filtering by any parameter.
bool isClickedCountry = false;
bool isClickedCity = false;
List countryUser = [];
List cityUser = [];
@override
void initState() {
super.initState();
filters = widget.initialState;
}
// A function to be able to select an element to filter.
void _handleCheckFilter(bool checked, String key, String value) {
final currentFilters = filters[key] ?? [];
if (checked) {
currentFilters.add(value);
} else {
currentFilters.remove(value);
}
setState(() {
filters[key] = currentFilters;
});
}
// Building a dialog box with filters.
@override
Widget build(BuildContext context) {
return SimpleDialog(
title: const Text('Filters',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25,
fontFamily: 'SuisseIntl',
fontWeight: FontWeight.w600,
)),
contentPadding: const EdgeInsets.all(16),
// Defining parameters for filtering.
children: [
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Column(children: [
InkWell(
onTap: () async {
countryUser = await getUserCountryOptions();
setState(() {
isClickedCountry = !isClickedCountry;
});
},
child: Row(children: [
Text('Select a country '.toString(),
style: const TextStyle(
fontSize: 18,
)),
SizedBox(
width: 80,
),
isClickedCountry
? const Icon(Icons.arrow_circle_up)
: const Icon(Icons.arrow_circle_down)
])),
!isClickedCountry
? Container()
: Column(
children: countryUser
.map(
(el) => CustomCheckboxTile(
value: filters['countryUser']?.contains(el) ??
false,
label: el,
onChange: (check) => _handleCheckFilter(
check, 'countryUser', el),
),
)
.toList())
]),
Column(children: [
InkWell(
onTap: () async {
cityUser = await getUserCityOptions();
setState(() {
isClickedCity = !isClickedCity;
});
},
child: Row(children: [
Text('Select a city'.toString(),
style: const TextStyle(
fontSize: 18,
)),
const SizedBox(
width: 120,
),
isClickedCity
? const Icon(Icons.arrow_circle_up)
: const Icon(Icons.arrow_circle_down)
])),
!isClickedCity
? Container()
: Column(
children: cityUser
.map((el) => CustomCheckboxTile(
value: filters['cityUser']?.contains(el) ??
false,
label: el,
onChange: (check) =>
_handleCheckFilter(check, 'cityUser', el),
))
.toList())
]),
// Building a button to apply parameters.
const SizedBox(
height: 10,
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
widget.onApplyFilters(filters);
needRefresh = true;
},
child: const Text('APPLY',
style: TextStyle(color: Colors.black)),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.grey),
)),
// Building a button to reset parameters.
const SizedBox(
height: 5,
),
ElevatedButton(
onPressed: () async {
setState(() {
filters.clear();
});
widget.onApplyFilters(filters);
},
child: const Text('RESET FILTERS',
style: TextStyle(color: Colors.black)),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.grey),
)),
],
)
]);
}
}
I get data from here
Future<List> getUserCountryOptions() async {
final optionsMap = await getFilterOptions(routes.getUserCountryOptions);
List options = [];
if (optionsMap.containsKey('countryUser')) {
options = optionsMap['options'];
}
return ['Georgia', 'Poland', 'Armenia'];
return options;
}
Future<List> getUserCityOptions() async {
final optionsMap = await getFilterOptions(routes.getUserCityOptions);
List options = [];
if (optionsMap.containsKey('cityUser')) {
options = optionsMap['options'];
}
return ['Tbilisi', "Batumi", 'Warsaw', "Kraków", "Wrocław", "Erevan", "Gyumri" ];
return options;
}
But I would like to have the following view of my filter window.
Since I've been learning flutter not so long ago, this is a very difficult task for me. Perhaps more experienced programmers can help me
Solution
I have created an example, you can adjust it to your needs:
class _TestState extends State<Test> {
List<FilterItem> children = [
FilterItem('Georgia', subitems: [
FilterItem('Tbilisi'),
FilterItem('Batumi'),
]),
FilterItem('Poland', subitems: [
FilterItem('Warsaw'),
FilterItem('Krakow'),
FilterItem('Wroclaw'),
]),
FilterItem('Armenia', subitems: [
FilterItem('Erevan'),
FilterItem('Gyumri'),
]),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: children.map(
(e) {
return Column(
children: [
CheckboxListTile(
controlAffinity: ListTileControlAffinity.leading,
contentPadding: EdgeInsets.zero,
title: Text(e.text),
value: e.selected,
onChanged: (value) => setState(() {
e.selected = value as bool;
}),
),
if (e.subitems.isNotEmpty)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
children: e.subitems
.map(
(e) => CheckboxListTile(
controlAffinity:
ListTileControlAffinity.leading,
contentPadding: EdgeInsets.zero,
value: e.selected,
title: Text(e.text),
onChanged: (value) => setState(() {
e.selected = value as bool;
}),
),
)
.toList(),
),
)
],
);
},
).toList(),
),
),
);
}
}
class FilterItem {
final String text;
bool selected;
List<FilterItem> subitems;
FilterItem(
this.text, {
this.selected = false,
this.subitems = const [],
});
}
Answered By - Ante Bule
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.