Keep dropdown open when active checkbox

Viewed 302

I have a code that is responsible for filtering by certain categories (I shortened it for ease of reading). When opening the filter window, the user sees these category names ('Select a brand', 'Select a operation system', 'Select a color' etc).

Next, the user can open the category (initially, the dropdown list is in the closed position.), and select the parameters from the drop-down list (and click the apply button). The next time you open the filter window, the checkboxes in front of the parameters remain, but the drop-down list collapses.

Tell me how to do it: if in any category there are options marked with a checkmark, so that the drop-down list will be open the next time the window with filters is opened.

     class FilterDialog extends StatefulWidget {
  final void Function(Map<String, List<String>?>) onApplyFilters;

  final Map<String, List<String>?> initialState;

  const FilterDialog({
    Key? key,
    required this.onApplyFilters,
    this.initialState = const {},
  }) : super(key: key);

  @override
  State<FilterDialog> createState() => _FilterDialogState();
}

class _FilterDialogState extends State<FilterDialog> {
  // 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 isClickedBrand = false;


  List manufacturer = [];


  @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(
      // Window title.

      title: const Text('Filters',
          textAlign: TextAlign.center,
          style: TextStyle(
            fontSize: 25,
            fontWeight: FontWeight.w600,
          )),
      contentPadding: const EdgeInsets.all(16),

      // Defining parameters for filtering.
      children: [
        Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            // Here and in subsequent Column, there will be a definition of parameters for filtering,
            // a title, the ability to hide/show the items of list
            Column(children: [
              InkWell(
                  onTap: () async {
                    manufacturer = await getManufacturerOptions();
                    setState(() {
                      isClickedBrand = !isClickedBrand;
                    });
                  },
                  child: Row(children: [
                    Text('Select a brand'.toString(),
                        style: const TextStyle(
                          fontSize: 18,
                        )),
                    const Spacer(),
                    isClickedBrand
                        ? const Icon(Icons.arrow_circle_up)
                        : const Icon(Icons.arrow_circle_down)
                  ])),
              !isClickedBrand
                  ? Container()
                  : Column(
                      children: manufacturer
                          .map(
                            (el) => CustomCheckboxTile(
                              value: filters['manufacturer']?.contains(el) ??
                                  false,
                              label: el,
                              onChange: (check) =>
                                  _handleCheckFilter(check, 'manufacturer', el),
                            ),
                          )
                          .toList())
            ]),
            const SizedBox(
              height: 5,
            ),



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

For example: the user clicks on the filter box, selects the brands to search for, and clicks the apply button. My task is that the next time the user opens the filter window, the categories with active checkboxes (in this example, the brand) are in an expanded state

enter image description here

2 Answers

The concept is, you need to check filter data with while opening dialog, To simplify the process I am using ExpansionTile. You can check this demo and customize the behavior and look.

Run on dartPad, Click fab to open dialog and touch outside the dialog to close this.

class ExTExpample extends StatefulWidget {
  ExTExpample({Key? key}) : super(key: key);

  @override
  State<ExTExpample> createState() => _ExTExpampleState();
}

class _ExTExpampleState extends State<ExTExpample> {
  // you can use map or model class or both,
  List<String> filter_data = [];
  List<String> brands = ["Apple", "SamSung"];
  List<String> os = ["iOS", "Android"];

  _showFilter() async {
    await showDialog(
      context: context,
      builder: (c) {
        // you can replace [AlertDialog]
        return AlertDialog(
          content: StatefulBuilder(
            builder: (context, setSBState) => SingleChildScrollView(
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  ExpansionTile(
                    title: const Text("Brand"),

                    /// check any of its's item is checked or not
                    initiallyExpanded: () {
                      // you can do different aproach
                      for (final f in brands) {
                        if (filter_data.contains(f)) return true;
                      }
                      return false;
                    }(),
                    children: [
                      ...brands.map(
                        (brandName) => CheckboxListTile(
                          value: filter_data.contains(brandName),
                          title: Text(brandName),
                          onChanged: (v) {
                            if (filter_data.contains(brandName)) {
                              filter_data.remove(brandName);
                            } else {
                              filter_data.add(brandName);
                            }

                            setSBState(() {});
                            //you need to reflect the main ui, also call `setState((){})`
                          },
                        ),
                      ),
                    ],
                  ),
                  ExpansionTile(
                    title: const Text("select OS"),

                    /// check any of its's item is checked or not
                    initiallyExpanded: () {
                      // you can do different aproach
                      for (final f in os) {
                        if (filter_data.contains(f)) return true;
                      }
                      return false;
                    }(),
                    children: [
                      ...os.map(
                        (osName) => CheckboxListTile(
                          value: filter_data.contains(osName),
                          title: Text(osName),
                          onChanged: (v) {
                            if (filter_data.contains(osName)) {
                              filter_data.remove(osName);
                            } else {
                              filter_data.add(osName);
                            }

                            setSBState(() {});
                            //you need to reflect the main ui, also call `setState((){})`
                          },
                        ),
                      ),
                    ],
                  ),
                ],
              ),
            ),
          ),
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: FloatingActionButton(
          onPressed: () {
            _showFilter();
          },
        ),
      ),
    );
  }
}

Whenever you open filter the isClickedBrand is False so it won't showed you a list. So the solution is : After selecting option from list, change the state of isClickedBrand state. I mean if it's true then it will show the list otherwise show container. Hope you get my point.

Related