icemanbsi / searchable_dropdown

MIT License
107 stars 166 forks source link

Error updating subsequent component #30

Open projetoswmltda opened 4 years ago

projetoswmltda commented 4 years ago

Dear sirs, I have a component that, after selecting an item of type object, loads a list into another subsequent component. The problem occurs when I change the top component and it does not clear the selected item in the subsequent component. How do I clean this selected item? Or is it really a bug?

Here is an excerpt from the code to exemplify

Top component

Container _buildTipoVeiculo() {
    return Container(
      transform: Matrix4.translationValues(0.0, -10.0, -10.0),
      child: Container(
        child: SearchableDropdown.single(
          items: tipoVeiculos,
          value: _anuncioData.tipoVeiculo,
          doneButton: "OK",
          label: _anuncioData.tipoVeiculo != null
              ? Container(
                  child: Text("Tipo de Veículo",
                      style:
                          TextStyle(color: Colors.teal[900], fontSize: 12.0)),
                  transform: Matrix4.translationValues(10.0, 20.0, 0.0))
              : null,
          hint: Padding(
            padding: const EdgeInsets.only(top: 4.0, bottom: 16.0),
            child: Text("Tipo de Veículo"),
          ),
          searchHint: "Selecione um item",
          closeButton: null,
          onChanged: (value) {
            setState(() {
              if (value != _anuncioData.tipoVeiculo) {
                _anuncioData.tipoVeiculo = value;
                _anuncioData.marca = null;
                initMarcas();
              }
            });
          },
          validator: _validateCampoObrigatorio,
          displayItem: (item, selected) {
            return (Row(children: [
              selected
                  ? Icon(
                      Icons.radio_button_checked,
                      color: Colors.grey,
                    )
                  : Icon(
                      Icons.radio_button_unchecked,
                      color: Colors.grey,
                    ),
              SizedBox(width: 7),
              Expanded(
                child: item,
              ),
            ]));
          },
          isExpanded: true,
        ),
      ),
    );
  }

Subsequent component

Container _buildMarca() {
    return Container(
      transform: Matrix4.translationValues(0.0, -10.0, -10.0),
      child: FutureBuilder<List<Marca>>(
          future: _futureMarcas,
          builder: (context, snapshot) {
            if (snapshot.hasError) print(snapshot.error);
            if (_anuncioData == null ||
                _anuncioData.tipoVeiculo == null ||
                snapshot.hasData) {
              return SearchableDropdown.single(
                items: convertDropDownMenuItemMarcas(snapshot),
                value: _anuncioData.marca,
                doneButton: "OK",
                label: _anuncioData.marca != null
                    ? Container(
                        child: Text("Marca",
                            style: TextStyle(
                                color: Colors.teal[900], fontSize: 12.0)),
                        transform: Matrix4.translationValues(10.0, 20.0, 0.0))
                    : null,
                hint: Padding(
                  padding: const EdgeInsets.only(bottom: 10.0),
                  child: Container(
                      child: Text("Marca"),
                      transform: Matrix4.translationValues(0.0, -2.0, 0.0)),
                ),
                searchHint: "Selecione um item",
                closeButton: null,
                onChanged: (value) {
                  if (value != _anuncioData.marca) {
                    setState(() {
                      _anuncioData.marca = value;
                      _anuncioData.modelo = null;
                      initModelos();
                    });
                  }
                },
                validator: _validateCampoObrigatorio,
                displayItem: (item, selected) {
                  return (Row(children: [
                    selected
                        ? Icon(
                            Icons.radio_button_checked,
                            color: Colors.grey,
                          )
                        : Icon(
                            Icons.radio_button_unchecked,
                            color: Colors.grey,
                          ),
                    SizedBox(width: 7),
                    Expanded(
                      child: item,
                    ),
                  ]));
                },
                isExpanded: true,
              );
            } else {
              return CircularProgressIndicator();
            }
          }),
    );
  }

Thanks.

lcuis commented 4 years ago

Dear @projetoswmltda ,

Thanks for reporting your issue.

I understand that a second widget is clearing the selected value of a first widget. If so, could it be that the following comment could help you? https://github.com/icemanbsi/searchable_dropdown/issues/29#issuecomment-604184851

projetoswmltda commented 4 years ago

Thanks for the quick feedback @lcuis , I managed to solve it by merging the code of your search_choices library with a small change.

Follows merged code (searchable_dropdown x search_choices):

updateSelectedItems({dynamic sel = const NotGiven()}) {
    List<int> updatedSelectedItems;
    if (widget.multipleSelection) {
      if (!(sel is NotGiven)) {
        updatedSelectedItems = sel as List<int>;
      } else {
        updatedSelectedItems =
            List<int>.from(widget.selectedItems ?? List<int>());
      }
    } else {
      T val = !(sel is NotGiven) ? sel as T : widget.value;
      if (val != null) {
        int i = indexFromValue(val);
        if (i != null && i != -1) {
          updatedSelectedItems = [i];
        }
      } else {
        updatedSelectedItems = null;
      }
      if (updatedSelectedItems == null) updatedSelectedItems = List<int>();
    }
    if (!widget.multipleSelection) {
      selectedItems.retainWhere((element) =>
          updatedSelectedItems.any((selected) => selected == element));
    }
    updatedSelectedItems.forEach((selected) {
      if (!selectedItems.any((element) => selected == element)) {
        selectedItems.add(selected);
      }
    });
  }

  @override
  void initState() {
    if (widget.multipleSelection) {
      selectedItems = List<int>.from(widget.selectedItems ?? []);
    } else if (widget.value != null) {
      int i = indexFromValue(widget.value);
      if (i != null && i != -1) {
        selectedItems = [i];
      }
    }
    if (selectedItems == null) selectedItems = [];
    updateSelectedItems();
    super.initState();
  }

  @override
  void didUpdateWidget(SearchableDropdown oldWidget) {
    super.didUpdateWidget(oldWidget);
    updateSelectedItems();
  }

After when have time, review a fix I made to resolve a problem in multiple selection of search_choices library in updateSelectedItems method:

if (!widget.multipleSelection) {
      selectedItems.retainWhere((element) =>
          updatedSelectedItems.any((selected) => selected == element));
    }

Thanks again

lcuis commented 4 years ago

Hello @projetoswmltda ,

Thanks a lot for your reply and for the fix proposal and for sharing your code!

With this code, you were able to solve your issue while still using searchable_dropdown?

projetoswmltda commented 4 years ago

Hi @lcuis, I actually tried it with both widgets, but it only worked on searchable_dropdown

I know that the code may still need some adjustments, but this way it works for me

lcuis commented 4 years ago

Hi @projetoswmltda ,

This is great news for searchable_dropdown, we should definitely give your fix a try whenever possible, thanks!