Update window with filters

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

enter image description here

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.

enter image description here

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

Answer Checked By – David Goodson (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published.