lcuis / search_choices

Widget to let the user search through a keyword string typed on a customizable keyboard in a single or multiple choices list presented as a dropdown in a dialog box or a menu.
MIT License
51 stars 42 forks source link

[FEATURE REQUEST] - Wrap items of SearchChoices.multiple() widget #109

Closed ramioooz closed 1 year ago

ramioooz commented 1 year ago

Hello developers.

First I would like to thank you very much for providing such a nice and smart widget. I have a suggestion for a feature here.

for the SearchChoices.multiple() widget. we wanted to be able to have the items in the multichoice list to be displayed in a wrap/grid. the only available option now is to have the items displayed in a list.

please have a look over my attached image to give you an idea about what we want. IMG_20230223_034306

we would like to have the items of SearchChoices.multiple() to be displayed in a wrap. something similar to what is shown in the second drawing in the image attached.

My regards. Rami

lcuis commented 1 year ago

Hello @ramioooz ,

Thank you very much for your message and request!

I believe this is covered by the selectedAggregateWidgetFn parameter. There is an example called "Multi dialog with count and wrap" as follows:

SearchChoices.multiple(
        items: items,
        selectedItems: selectedItemsMultiDialogWithCountAndWrap,
        hint: "Select items",
        searchHint: "Select items",
        onChanged: (value) {
          setState(() {
            selectedItemsMultiDialogWithCountAndWrap = value;
          });
        },
        isExpanded: true,
        selectedValueWidgetFn: (item) {
          return (Container(
            margin: const EdgeInsets.all(15.0),
            padding: const EdgeInsets.all(3.0),
            decoration:
                BoxDecoration(border: Border.all(color: Colors.blueAccent)),
            child: Text(
              item,
              overflow: TextOverflow.ellipsis,
            ),
          ));
        },
        selectedAggregateWidgetFn: (List<Widget> list) {
          return (Column(children: [
            Text("${list.length} items selected"),
            Wrap(children: list),
          ]));
        },
      )

With this result: https://searchchoices.jod.li/Multi%20dialog%20with%20count%20and%20wrap.gif

Cheers!

ramioooz commented 1 year ago

Hello @lcuis , Thank you very much for your reply.

selectedAggregateWidgetFn parameter helps us show already-selected items in Wrap which is great. but I am not talking about that... I am talking about the stage (before selecting the items) where you open a dialog to search and select your choices. items in that popup dialog are listed vertically. we wanted to be able to have the items in a grid/wrap.

My Regards.

lcuis commented 1 year ago

Oh, indeed, sorry, let me check.

lcuis commented 1 year ago

Indeed, for this to work easily, there would need to be an additional callback to replace the listDisplay method in _DropdownDialogState class: https://github.com/lcuis/search_choices/blob/6c6a430b9a40a702b38a0b37882e1d7134adfbe3/lib/src/dropdown/dropdown_dialog.dart#L713-L748

Thanks for mentioning this!

I hope this can be done soon.

Macacoazul01 commented 1 year ago

@lcuis there is something like this being done here: https://pub.dev/packages/multi_dropdown @ramioooz correct me if this isn't what you are expecting

ramioooz commented 1 year ago

@lcuis there is something like this being done here: https://pub.dev/packages/multi_dropdown @ramioooz correct me if this isn't what you are expecting

not really. the example display selected items in a wrap. we can do that in your widget using selectedAggregateWidgetFn parameter. but again this is not the issue. we are talking about the items displayed inside the popup dialog or menu. the dialog shows search results vertically which is fine in most cases. but sometimes we want to show search results in a wrap. (as an inventory of items a user can select from) we are using buildDropDownDialogto customize the dialog but we can not put list items in a wrap. check this snippet of our menu code:

buildDropDownDialog: (
          Widget titleBar,
          Widget searchBar,
          Widget list,
          Widget closeButton,
          BuildContext dropDownContext,
        ) {
          return (AnimatedContainer(
            padding: MediaQuery.of(dropDownContext).viewInsets,
            duration: const Duration(milliseconds: 300),
            child: Card(
              color: Colors.grey[100],
              margin: const EdgeInsets.symmetric(vertical: 80, horizontal: 80),
              child: Container(
                padding:
                    const EdgeInsets.symmetric(vertical: 20, horizontal: 20),
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.start,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  mainAxisSize: MainAxisSize.min,
                  children: <Widget>[
                    searchBar,
                    list, // <-- we want to have this as a wrap. right now it is an Expanded widget
                    // closeButton,
                  ],
                ),
              ),
            ),
          ));
        },

the photos attaches show the current result and the wanted result. A. current result 111 B. wanted result 222

Thank you.

lcuis commented 1 year ago

Thanks @Macacoazul01 for the link. This is an interesting project.

Thanks @ramioooz for the additional explanation and examples.

lcuis commented 1 year ago

Hi @ramioooz ,

Here is an example with the upcoming new parameter searchResultDisplayFn:

        searchResultDisplayFn: ({
          required List<Tuple3<int, DropdownMenuItem, bool>> itemsToDisplay,
          required ScrollController scrollController,
          required bool thumbVisibility,
          required Widget emptyListWidget,
          required void Function(int index, dynamic value, bool itemSelected)
              itemTapped,
          required Widget Function(DropdownMenuItem item, bool isItemSelected)
              displayItem,
        }) {
          return Expanded(
              child: itemsToDisplay.length == 0
                  ? emptyListWidget
                  : SingleChildScrollView(
                      child: Wrap(
                          spacing: 10,
                          children: itemsToDisplay.map(
                            (Tuple3<int, DropdownMenuItem, bool> item) {
                              return Padding(
                                padding:
                                    const EdgeInsets.symmetric(vertical: 8.0),
                                child: InkWell(
                                  onTap: () {
                                    itemTapped(
                                      item.item1,
                                      item.item2.value,
                                      item.item3,
                                    );
                                  },
                                  child: Container(
                                    decoration: BoxDecoration(
                                        border: Border.all(
                                      color: Colors.grey,
                                      width: 5,
                                    )),
                                    child: Row(
                                      mainAxisSize: MainAxisSize.min,
                                      children: [
                                        Padding(
                                          padding: const EdgeInsets.symmetric(
                                              horizontal: 8.0),
                                          child: item.item2,
                                        ),
                                      ],
                                    ),
                                  ),
                                ),
                              );
                            },
                          ).toList()),
                    ));
        },

With this result: image

I will commit and publish as soon as possible.

lcuis commented 1 year ago

Release 2.2.4 has just been published to pub.dev with the previously mentioned new parameter.

ramioooz commented 1 year ago

Hello @lcuis

I can't thank you enough for putting the effort into bringing the idea into the material world. You just exceeded my expectations. I updated your plugin in my project and added the new parameter searchResultDisplayFnto my code as you suggested with slight modifications and here is the end result.

searchResultDisplayFn: ({
          required List<Tuple3<int, DropdownMenuItem, bool>> itemsToDisplay,
          required ScrollController scrollController,
          required bool thumbVisibility,
          required Widget emptyListWidget,
          required void Function(int index, dynamic value, bool itemSelected)
              itemTapped,
          required Widget Function(DropdownMenuItem item, bool isItemSelected)
              displayItem,
        }) {
          return Expanded(
            child: itemsToDisplay.isEmpty
                ? emptyListWidget
                : SingleChildScrollView(
                    child: Wrap(
                      spacing: 10,
                      // runSpacing: 10,
                      children: itemsToDisplay.map(
                        (Tuple3<int, DropdownMenuItem, bool> item) {
                          return Padding(
                            padding: const EdgeInsets.symmetric(vertical: 5),
                            child: InkWell(
                              onTap: () {
                                itemTapped(
                                  item.item1,
                                  item.item2.value,
                                  item.item3,
                                );
                              },
                              child: Container(
                                decoration: BoxDecoration(
                                  border: Border.all(color: Colors.grey),
                                  borderRadius: const BorderRadius.all(
                                    Radius.circular(5),
                                  ),
                                ),
                                child: Row(
                                  mainAxisSize: MainAxisSize.min,
                                  children: [
                                    // Checkbox(
                                    //     value: item.item3, onChanged: null),
                                    item.item3
                                        ? Icon(
                                            Icons.check_box,
                                            color:
                                                Theme.of(context).primaryColor,
                                          )
                                        : const Icon(
                                            Icons.check_box_outline_blank,
                                            color: Colors.grey,
                                          ),
                                    item.item2,
                                  ],
                                ),
                              ),
                            ),
                          );
                        },
                      ).toList(),
                    ),
                  ),
          );
        },

555

Thank you very much 👏👏👏👏👏

lcuis commented 1 year ago

Thanks a lot @ramioooz for the confirmation and for your message!

Enjoy!