icemanbsi / searchable_dropdown

MIT License
107 stars 162 forks source link

Is it possible to get the value of the typed search string ? #122

Open kfchengkf opened 3 years ago

kfchengkf commented 3 years ago

Hi,

I would like to know it it is possible to check if the current typed string matches anything. If no, then I would enable a button which can get the current typed search string, (so that I can further process it, e.g. create a new item based on the typed search string).

Thanks.

lcuis commented 3 years ago

Hello @kfchengkf ,

You can use searchFn argument. Here is an example with search_choices plugin but it should work the same with searchable_dropdown plugin:

SearchChoices.multiple(
        items: items,
        selectedItems: selectedItemsMultiCustomDisplayDialog,
        onChanged: (value) {
          setState(() {
            selectedItemsMultiCustomDisplayDialog = value;
          });
        },
        searchFn: (String keyword, items) {
          print("Typed keyword: $keyword");
          List<int> ret = [];
          if (items != null && keyword.isNotEmpty) {
            keyword.split(" ").forEach((k) {
              int i = 0;
              items.forEach((item) {
                if (k.isNotEmpty &&
                    (item.value
                        .toString()
                        .toLowerCase()
                        .contains(k.toLowerCase()))) {
                  ret.add(i);
                }
                i++;
              });
            });
          }
          if (keyword.isEmpty) {
            ret = Iterable<int>.generate(items.length).toList();
          }
          return (ret);
        },
        isExpanded: true,
      )
kfchengkf commented 3 years ago

Hi locus, Is it possible to customize either the done button or the close button to reference the typed search string, as mentioned previously, such that if the search string doesn't match, the button would be enabled and, when clicked, open a new widget with the search string in one field, and the user able to enter values for other fields ?

lcuis commented 3 years ago

The UI must propose to the user the addition of an object to the list in case the keyword doesn’t match?

kfchengkf commented 3 years ago

No. Only the field to be search should be added. The search field is the name of the object. If it doesn't exist, I can call the "Close" button that popup another screen with the name field preset. Other fields can then be edited on this screen too. I wonder if a "global" variable is needed to save the typed string in the searchFn, and then checked in the "Close" button function.

lcuis commented 3 years ago

Hi @kfchengkf ,

I believe that the following code does what you are asking for. If not, I apologize for my misunderstanding.

search_choices-add_missing_keyword

  List<DropdownMenuItem> editableItems = [];
  final _formAddMissingKey = GlobalKey<FormState>();
  final _fieldAddMissingKey = GlobalKey<FormFieldState>();
  String inputString = "";
  List<int> selectedItemsMultiAddMissingDialog = [];
  List<DropdownMenuItem> editableItems = [];
  bool multiAddMissingDialogPropose=false;
  String multiAddMissingDialogProposeKeyword="";

...
  addItemDialogWithProposal(String proposal) async {
    return await showDialog(
      context: MyApp.navKey.currentState?.overlay?.context ?? context,
      builder: (BuildContext alertContext) {
        return (AlertDialog(
          title: Text("Add an item"),
          content: Form(
            key: _formAddMissingKey,
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                TextFormField(
                  key: _fieldAddMissingKey,
                  validator: (value) {
                    return ((value?.length ?? 0) < 6
                        ? "must be at least 6 characters long"
                        : null);
                  },
                  initialValue: proposal,
                  onChanged: (value) {
                    inputString = value;
                  },
                  onSaved: (value){
                    inputString = value!;
                  },
                  autofocus: true,
                ),
                TextButton(
                  onPressed: () {
                    if (_formAddMissingKey.currentState?.validate() ?? false) {
                      _fieldAddMissingKey.currentState?.save();
                      setState(() {
                        editableItems.add(DropdownMenuItem(
                          child: Text(inputString),
                          value: inputString,
                        ));
                      });
                      Navigator.pop(alertContext, inputString);
                    }
                  },
                  child: Text("Ok"),
                ),
                TextButton(
                  onPressed: () {
                    Navigator.pop(alertContext, null);
                  },
                  child: Text("Cancel"),
                ),
              ],
            ),
          ),
        ));
      },
    );
  }
...
SearchChoices.multiple(
        items: editableItems,
        selectedItems: selectedItemsMultiAddMissingDialog,
        onChanged: (value) {
          setState(() {
            selectedItemsMultiAddMissingDialog = value;
          });
        },
        searchFn: (String keyword, items) {
          List<int> ret = [];
          if (items != null && keyword.isNotEmpty) {
            keyword.split(" ").forEach((k) {
              int i = 0;
              items.forEach((item) {
                if (k.isNotEmpty &&
                    (item.value
                        .toString()
                        .toLowerCase()
                        .contains(k.toLowerCase()))) {
                  ret.add(i);
                }
                i++;
              });
            });
          }
          if (keyword.isEmpty) {
            ret = Iterable<int>.generate(items.length).toList();
          }
          multiAddMissingDialogPropose=ret.length==0;
          multiAddMissingDialogProposeKeyword=keyword;
          return (ret);
        },
        isExpanded: true,
        disabledHint: (Function updateParent) {
          return (TextButton(
            onPressed: () {
              addItemDialogWithProposal("").then((value) async {
                if (value != null) {
                  selectedItemsMultiAddMissingDialog = [0];
                  updateParent(selectedItemsMultiAddMissingDialog);
                }
              });
            },
            child: Text("No choice, click to add one"),
          ));
        },
        closeButton: (List<int> values, BuildContext closeContext,
            Function updateParent) {
          return (editableItems.length >= 100||!multiAddMissingDialogPropose
              ? SizedBox.shrink()
              : TextButton(
            onPressed: () {
              addItemDialogWithProposal(multiAddMissingDialogProposeKeyword).then((value) async {
                if (value != null) {
                  int itemIndex = editableItems
                      .indexWhere((element) => element.value == value);
                  if (itemIndex != -1) {
                    selectedItemsMultiAddMissingDialog.add(itemIndex);
                    Navigator.pop(
                        MyApp.navKey.currentState?.overlay?.context ??
                            context);
                    updateParent(selectedItemsMultiAddMissingDialog);
                  }
                }
              });
            },
            child: Text("Add and select item"),
          ));
        },
      );
...

Note that I didn't try this with plugin searchable_dropdown but with search_choices. Maybe this works with both?

kfchengkf commented 3 years ago

Hi Icuis, Yes, exactly.
Thanks

lcuis commented 3 years ago

You’re welcome @kfchengkf !