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.

Overview

searchable_dropdown

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.

Platforms

This widget has been successfully tested on iOS, Android and Chrome.

Examples

The following examples are extracted from the example project available in the repository.

Gallery

See code below.

Example name Demonstration
Single dialog Single dialog
Multi dialog Multi dialog
Single done button
dialog
Single done button dialog
Multi custom display
dialog
Multi custom display dialog
Multi select 3 dialog Multi select 3 dialog
Single menu Single menu
Multi menu Multi menu
Multi menu select
all/none
Multi menu select all or none
Multi dialog select
all/none without clear
Multi dialog select all or none without clear
Single dialog custom
keyboard
Single dialog custom keyboard
Single dialog object Single dialog object
Single dialog overflow Single dialog overflow
Single dialog readOnly Single dialog readOnly
Single dialog disabled Single dialog disabled

Code

Plugin usage

Add to your pubspec.yaml in the dependencies section:

  searchable_dropdown:

Get packages with command:

flutter packages get

Import:

import 'package:searchable_dropdown/searchable_dropdown.dart';

Call either the single choice or the multiple choice constructor.

Single choice constructor

Search choices Widget with a single choice that opens a dialog or a menu to let the user do the selection conveniently with a search.

factory SearchableDropdown.single({
    Key key,
    @required List<DropdownMenuItem<T>> items,
    @required Function onChanged,
    T value,
    TextStyle style,
    dynamic searchHint,
    dynamic hint,
    dynamic disabledHint,
    dynamic icon = const Icon(Icons.arrow_drop_down),
    dynamic underline,
    dynamic doneButton,
    dynamic label,
    dynamic closeButton = "Close",
    bool displayClearIcon = true,
    Icon clearIcon = const Icon(Icons.clear),
    Color iconEnabledColor,
    Color iconDisabledColor,
    double iconSize = 24.0,
    bool isExpanded = false,
    bool isCaseSensitiveSearch = false,
    Function searchFn,
    Function onClear,
    Function selectedValueWidgetFn,
    TextInputType keyboardType = TextInputType.text,
    Function validator,
    bool assertUniqueValue = true,
    Function displayItem,
    bool dialogBox = true,
    BoxConstraints menuConstraints,
    bool readOnly: false,
    Color menuBackgroundColor,
}
)
  • items with child: Widget displayed ; value: any object with .toString() used to match search keyword.
  • onChanged Function with parameter: value not returning executed after the selection is done.
  • value value to be preselected.
  • style used for the hint if it is given is String.
  • searchHint String|Widget|Function with no parameter returning String|Widget displayed at the top of the search dialog box.
  • hint String|Widget|Function with no parameter returning String|Widget displayed before any value is selected or after the selection is cleared.
  • disabledHint String|Widget|Function with no parameter returning String|Widget displayed instead of hint when the widget is displayed.
  • icon String|Widget|Function with parameter: value returning String|Widget displayed next to the selected item or the hint if none.
  • underline String|Widget|Function with parameter: value returning String|Widget displayed below the selected item or the hint if none.
  • doneButton String|Widget|Function with parameter: value returning String|Widget displayed at the top of the search dialog box.
  • label String|Widget|Function with parameter: value returning String|Widget displayed above the selected item or the hint if none.
  • closeButton String|Widget|Function with parameter: value returning String|Widget displayed at the bottom of the search dialog box.
  • displayClearIcon whether or not to display an icon to clear the selected value.
  • clearIcon Icon to be used for clearing the selected value.
  • iconEnabledColor Color to be used for enabled icons.
  • iconDisabledColor Color to be used for disabled icons.
  • iconSize for the icons next to the selected value (icon and clearIcon).
  • isExpanded can be necessary to avoid pixel overflows (zebra symptom).
  • isCaseSensitiveSearch only used when searchFn is not specified.
  • searchFn Function with parameters: keyword, items returning List as the list of indexes for the items to be displayed.
  • onClear Function with no parameter not returning executed when the clear icon is tapped.
  • selectedValueWidgetFn Function with parameter: item returning Widget to be used to display the selected value.
  • keyboardType used for the search.
  • validator Function with parameter: value returning String displayed below selected value when not valid and null when valid.
  • assertUniqueValue whether to run a consistency check of the list of items.
  • displayItem Function with parameters: item, selected returning Widget to be displayed in the search list.
  • dialogBox whether the search should be displayed as a dialog box or as a menu below the selected value if any.
  • menuConstraints BoxConstraints used to define the zone where to display the search menu. Example: BoxConstraints.tight(Size.fromHeight(250)) . Not to be used for dialogBox = true.
  • readOnly bool whether to let the user choose the value to select or just present the selected value if any.
  • menuBackgroundColor Color background color of the menu whether in dialog box or menu mode.

Multiple choice constructor

Search choices Widget with a multiple choice that opens a dialog or a menu to let the user do the selection conveniently with a search.

SearchableDropdown<T>.multiple(
{
    Key key,
    @required List<DropdownMenuItem<T>> items,
    @required Function onChanged,
    List<int> selectedItems: const [],
    TextStyle style,
    dynamic searchHint,
    dynamic hint,
    dynamic disabledHint,
    dynamic icon: const Icon(Icons.arrow_drop_down),
    dynamic underline,
    dynamic doneButton: "Done",
    dynamic label,
    dynamic closeButton: "Close",
    bool displayClearIcon: true,
    Icon clearIcon: const Icon(Icons.clear),
    Color iconEnabledColor,
    Color iconDisabledColor,
    double iconSize: 24.0,
    bool isExpanded: false,
    bool isCaseSensitiveSearch: false,
    Function searchFn,
    Function onClear,
    Function selectedValueWidgetFn,
    TextInputType keyboardType: TextInputType.text,
    Function validator,
    Function displayItem,
    bool dialogBox: true,
    BoxConstraints menuConstraints,
    bool readOnly: false,
    Color menuBackgroundColor,
}
)
  • items with child: Widget displayed ; value: any object with .toString() used to match search keyword.
  • onChanged Function with parameter: selectedItems not returning executed after the selection is done.
  • selectedItems indexes of items to be preselected.
  • style used for the hint if it is given is String.
  • searchHint String|Widget|Function with no parameter returning String|Widget displayed at the top of the search dialog box.
  • hint String|Widget|Function with no parameter returning String|Widget displayed before any value is selected or after the selection is cleared.
  • disabledHint String|Widget|Function with no parameter returning String|Widget displayed instead of hint when the widget is displayed.
  • icon String|Widget|Function with parameter: selectedItems returning String|Widget displayed next to the selected items or the hint if none.
  • underline String|Widget|Function with parameter: selectedItems returning String|Widget displayed below the selected items or the hint if none.
  • doneButton String|Widget|Function with parameter: selectedItems returning String|Widget displayed at the top of the search dialog box. Cannot be null in multiple selection mode.
  • label String|Widget|Function with parameter: selectedItems returning String|Widget displayed above the selected items or the hint if none.
  • closeButton String|Widget|Function with parameter: selectedItems returning String|Widget displayed at the bottom of the search dialog box.
  • displayClearIcon whether or not to display an icon to clear the selected values.
  • clearIcon Icon to be used for clearing the selected values.
  • iconEnabledColor Color to be used for enabled icons.
  • iconDisabledColor Color to be used for disabled icons.
  • iconSize for the icons next to the selected values (icon and clearIcon).
  • isExpanded can be necessary to avoid pixel overflows (zebra symptom).
  • isCaseSensitiveSearch only used when searchFn is not specified.
  • searchFn Function with parameters: keyword, items returning List as the list of indexes for the items to be displayed.
  • onClear Function with no parameter not returning executed when the clear icon is tapped.
  • selectedValueWidgetFn Function with parameter: item returning Widget to be used to display the selected values.
  • keyboardType used for the search.
  • validator Function with parameter: selectedItems returning String displayed below selected values when not valid and null when valid.
  • displayItem Function with parameters: item, selected returning Widget to be displayed in the search list.
  • dialogBox whether the search should be displayed as a dialog box or as a menu below the selected values if any.
  • menuConstraints BoxConstraints used to define the zone where to display the search menu. Example: BoxConstraints.tight(Size.fromHeight(250)) . Not to be used for dialogBox = true.
  • readOnly bool whether to let the user choose the value to select or just present the selected value if any.
  • menuBackgroundColor Color background color of the menu whether in dialog box or menu mode.

Example app usage

Clone repository:

git clone https://github.com/icemanbsi/searchable_dropdown.git

Go to plugin folder:

cd searchable_dropdown

Optionally enable web:

flutter config --enable-web

Create project:

flutter create .

To run automated tests:

flutter test

Optionally generate documentation:

pub global activate dartdoc
dartdoc

Go to example app folder:

cd example

To run web:

run -d chrome

To build web to folder build/web:

flutter build web

To run on a connected device:

flutter run

To build Android app to build/app/outputs/apk/release/app-release.apk:

flutter build apk

To build iOS app on Mac:

flutter build ios

Single dialog

      SearchableDropdown.single(
        items: items,
        value: selectedValue,
        hint: "Select one",
        searchHint: "Select one",
        onChanged: (value) {
          setState(() {
            selectedValue = value;
          });
        },
        isExpanded: true,
      ),

Multi dialog

      SearchableDropdown.multiple(
        items: items,
        selectedItems: selectedItems,
        hint: Padding(
          padding: const EdgeInsets.all(12.0),
          child: Text("Select any"),
        ),
        searchHint: "Select any",
        onChanged: (value) {
          setState(() {
            selectedItems = value;
          });
        },
        closeButton: (selectedItems) {
          return (selectedItems.isNotEmpty
              ? "Save ${selectedItems.length == 1 ? '"' + items[selectedItems.first].value.toString() + '"' : '(' + selectedItems.length.toString() + ')'}"
              : "Save without selection");
        },
        isExpanded: true,
      ),

Single done button dialog

      SearchableDropdown.single(
        items: items,
        value: selectedValue,
        hint: "Select one",
        searchHint: "Select one",
        onChanged: (value) {
          setState(() {
            selectedValue = value;
          });
        },
        doneButton: "Done",
        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,
      ),

Multi custom display dialog

ret = List (); if (keyword != null && 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 .generate(items.length).toList(); } return (ret); }, clearIcon: Icon(Icons.clear_all), icon: Icon(Icons.arrow_drop_down_circle), label: "Label for multi", underline: Container( height: 1.0, decoration: BoxDecoration( border: Border(bottom: BorderSide(color: Colors.teal, width: 3.0))), ), iconDisabledColor: Colors.brown, iconEnabledColor: Colors.indigo, isExpanded: true, ), ">
      SearchableDropdown.multiple(
        items: items,
        selectedItems: selectedItems,
        hint: Padding(
          padding: const EdgeInsets.all(12.0),
          child: Text("Select any"),
        ),
        searchHint: "Select any",
        onChanged: (value) {
          setState(() {
            selectedItems = value;
          });
        },
        displayItem: (item, selected) {
          return (Row(children: [
            selected
                ? Icon(
                    Icons.check,
                    color: Colors.green,
                  )
                : Icon(
                    Icons.check_box_outline_blank,
                    color: Colors.grey,
                  ),
            SizedBox(width: 7),
            Expanded(
              child: item,
            ),
          ]));
        },
        selectedValueWidgetFn: (item) {
          return (Center(
              child: Card(
                  shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(10),
                    side: BorderSide(
                      color: Colors.brown,
                      width: 0.5,
                    ),
                  ),
                  margin: EdgeInsets.all(12),
                  child: Padding(
                    padding: const EdgeInsets.all(8),
                    child: Text(item.toString()),
                  ))));
        },
        doneButton: (selectedItemsDone, doneContext) {
          return (RaisedButton(
              onPressed: () {
                Navigator.pop(doneContext);
                setState(() {});
              },
              child: Text("Save")));
        },
        closeButton: null,
        style: TextStyle(fontStyle: FontStyle.italic),
        searchFn: (String keyword, items) {
          List<int> ret = List<int>();
          if (keyword != null && 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);
        },
        clearIcon: Icon(Icons.clear_all),
        icon: Icon(Icons.arrow_drop_down_circle),
        label: "Label for multi",
        underline: Container(
          height: 1.0,
          decoration: BoxDecoration(
              border:
                  Border(bottom: BorderSide(color: Colors.teal, width: 3.0))),
        ),
        iconDisabledColor: Colors.brown,
        iconEnabledColor: Colors.indigo,
        isExpanded: true,
      ),

Multi select 3 dialog

      SearchableDropdown.multiple(
        items: items,
        selectedItems: selectedItems,
        hint: "Select 3 items",
        searchHint: "Select 3",
        validator: (selectedItemsForValidator) {
          if (selectedItemsForValidator.length != 3) {
            return ("Must select 3");
          }
          return (null);
        },
        onChanged: (value) {
          setState(() {
            selectedItems = value;
          });
        },
        doneButton: (selectedItemsDone, doneContext) {
          return (RaisedButton(
              onPressed: selectedItemsDone.length != 3
                  ? null
                  : () {
                      Navigator.pop(doneContext);
                      setState(() {});
                    },
              child: Text("Save")));
        },
        closeButton: (selectedItems) {
          return (selectedItems.length == 3 ? "Ok" : null);
        },
        isExpanded: true,
      ),

Single menu

      SearchableDropdown.single(
        items: items,
        value: selectedValue,
        hint: "Select one",
        searchHint: null,
        onChanged: (value) {
          setState(() {
            selectedValue = value;
          });
        },
        dialogBox: false,
        isExpanded: true,
        menuConstraints: BoxConstraints.tight(Size.fromHeight(350)),
      ),

Multi menu

      SearchableDropdown.multiple(
        items: items,
        selectedItems: selectedItems,
        hint: "Select any",
        searchHint: "",
        doneButton: "Close",
        closeButton: SizedBox.shrink(),
        onChanged: (value) {
          setState(() {
            selectedItems = value;
          });
        },
        dialogBox: false,
        isExpanded: true,
        menuConstraints: BoxConstraints.tight(Size.fromHeight(350)),
      ),

Multi menu select all/none

[ RaisedButton( onPressed: () { setState(() { selectedItems.clear(); selectedItems.addAll( Iterable .generate(items.length).toList()); }); }, child: Text("Select all")), RaisedButton( onPressed: () { setState(() { selectedItems.clear(); }); }, child: Text("Select none")), ], ); }, isExpanded: true, menuConstraints: BoxConstraints.tight(Size.fromHeight(350)), ), ">
      SearchableDropdown.multiple(
        items: items,
        selectedItems: selectedItems,
        hint: "Select any",
        searchHint: "Select any",
        onChanged: (value) {
          setState(() {
            selectedItems = value;
          });
        },
        dialogBox: false,
        closeButton: (selectedItemsClose) {
          return Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: <Widget>[
              RaisedButton(
                  onPressed: () {
                    setState(() {
                      selectedItems.clear();
                      selectedItems.addAll(
                          Iterable<int>.generate(items.length).toList());
                    });
                  },
                  child: Text("Select all")),
              RaisedButton(
                  onPressed: () {
                    setState(() {
                      selectedItems.clear();
                    });
                  },
                  child: Text("Select none")),
            ],
          );
        },
        isExpanded: true,
        menuConstraints: BoxConstraints.tight(Size.fromHeight(350)),
      ),

Multi dialog select all/none without clear

[ RaisedButton( onPressed: () { setState(() { selectedItems.clear(); selectedItems.addAll( Iterable .generate(items.length).toList()); }); }, child: Text("Select all")), RaisedButton( onPressed: () { setState(() { selectedItems.clear(); }); }, child: Text("Select none")), ], ); }, isExpanded: true, ), ">
      SearchableDropdown.multiple(
        items: items,
        selectedItems: selectedItems,
        hint: "Select any",
        searchHint: "Select any",
        displayClearIcon: false,
        onChanged: (value) {
          setState(() {
            selectedItems = value;
          });
        },
        dialogBox: true,
        closeButton: (selectedItemsClose) {
          return Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: <Widget>[
              RaisedButton(
                  onPressed: () {
                    setState(() {
                      selectedItems.clear();
                      selectedItems.addAll(
                          Iterable<int>.generate(items.length).toList());
                    });
                  },
                  child: Text("Select all")),
              RaisedButton(
                  onPressed: () {
                    setState(() {
                      selectedItems.clear();
                    });
                  },
                  child: Text("Select none")),
            ],
          );
        },
        isExpanded: true,
      ),

Single dialog custom keyboard

      SearchableDropdown.single(
        items: Iterable<int>.generate(20).toList().map((i) {
          return (DropdownMenuItem(
            child: Text(i.toString()),
            value: i.toString(),
          ));
        }).toList(),
        value: selectedValue,
        hint: "Select one number",
        searchHint: "Select one number",
        onChanged: (value) {
          setState(() {
            selectedValue = value;
          });
        },
        dialogBox: true,
        keyboardType: TextInputType.number,
        isExpanded: true,
      ),

Single dialog object

      SearchableDropdown.single(
        items: ExampleNumber.list.map((exNum) {
          return (DropdownMenuItem(
              child: Text(exNum.numberString), value: exNum));
        }).toList(),
        value: selectedNumber,
        hint: "Select one number",
        searchHint: "Select one number",
        onChanged: (value) {
          setState(() {
            selectedNumber = value;
          });
        },
        dialogBox: true,
        isExpanded: true,
      ),

Single dialog overflow

      SearchableDropdown.single(
        items: [
          DropdownMenuItem(
            child: Text(
                "way too long text for a smartphone at least one that goes in a normal sized pair of trousers but maybe not for a gigantic screen like there is one at my cousin's home in a very remote country where I 
wouldn't want to go right now"),
            value:
                "way too long text for a smartphone at least one that goes in a normal sized pair of trousers but maybe not for a gigantic screen like there is one at my cousin's home in a very remote country where I 
wouldn't want to go right now",
          )
        ],
        value: "",
        hint: "Select one",
        searchHint: "Select one",
        onChanged: (value) {
          setState(() {
            selectedValue = value;
          });
        },
        dialogBox: true,
        isExpanded: true,
      ),

Single dialog readOnly

      SearchableDropdown.single(
        items: [
          DropdownMenuItem(
            child: Text(
                "one item"),
            value:
            "one item",
          )
        ],
        value: "one item",
        hint: "Select one",
        searchHint: "Select one",
        disabledHint: "Disabled",
        onChanged: (value) {
          setState(() {
            selectedValue = value;
          });
        },
        dialogBox: true,
        isExpanded: true,
        readOnly: true,
      ),

Single dialog disabled

      SearchableDropdown.single(
        items: [
          DropdownMenuItem(
            child: Text(
                "one item"),
            value:
            "one item",
          )
        ],
        value: "one item",
        hint: "Select one",
        searchHint: "Select one",
        disabledHint: "Disabled",
        onChanged: null,
        dialogBox: true,
        isExpanded: true,
      ),

Feature requests/comments/questions/bugs

Feel free to log your feature requests/comments/questions/bugs here: https://github.com/icemanbsi/searchable_dropdown/issues

Contributions

We would be happy to merge pull request proposals provided that:

  • they don't break the compilation
  • they pass the automated testing
  • they provide the relevant adaptations to documentation and automated testing
  • they bring value
  • they don't completely transform the code
  • they are readable (though, I enjoy https://www.ioccc.org/ as a contest full of curiosities)

Contributions and forks are very welcome!

In your pull request, feel free to add your line in the contributors section below:

Contributors

CI/CD

Continuous integration/deployment status: CI-CD

Comments
  • How to Search data from api list

    How to Search data from api list

    here is my data list from api

    
      Future<String> getSWData() async {
        var res = await http .get(Uri.encodeFull(url), headers: {"Accept": "application/json"});
        var resBody = json.decode(res.body);
    
        setState(() {
          data = resBody;
        });
    
        return "Sucess";
      }
    `
    ```
    and here is my dropdown 
    
     ```
     children: <Widget>[
                 SearchableDropdown.single(
                 hint:Text("Select Country"),
                  isExpanded: true,
              items: data.map((item) {
                return  DropdownMenuItem(
                 
                  child:  Text(item['name']),
                  value: item['id'].toString(),
                );
          
              }).toList(),
              onChanged: (newVal) {
                setState(() {
                  _countryname = newVal;
                  city.clear();
                  _myState=null;
                  this.getCity(int.parse(_countryname));
                });
              },
              value:_countryname,
            ),
             ],
    ```
    
    it's load data from api but when i search i m not getting search data is there is any extra step for search? the package should be dynamic like jquery select2
    
    
    opened by atta1234 24
  • Model Class Support

    Model Class Support

    I tried to use the package with a model class, but when I search, it doesn't appear in the list. Do you have a model example? Or does this plugin really not support it?

    My code:

    class FabricanteOleo {
      String nome;
      int id;
    
      FabricanteOleo({this.nome, this.id});
    
      FabricanteOleo.fromJson(json) {
        nome = json['nome'];
        id = json['id'];
      }
    
      Map<String, dynamic> toJson() {
        final Map<String, dynamic> data = new Map<String, dynamic>();
        data['nome'] = this.nome;
        data['id'] = this.id;
        return data;
      }
    }
    
     _dropSearch() {
        return new SearchableDropdown<FabricanteOleo>(
          isCaseSensitiveSearch: true,
          items: listaFabricantes.map((fab){
            return DropdownMenuItem<FabricanteOleo>(
              child: new Text(fab.nome),
              value: fab,
            );
          }).toList(),
          value: fabricanteOleoSelecionado,
          hint: new Text('Select One'),
          searchHint: new Text(
            'Select One',
            style: new TextStyle(fontSize: 20),
          ),
          onChanged: (FabricanteOleo value) {
            setState(() {
              this.fabricanteOleoSelecionado = value;
            });
          },
        );
    

    Captura de Tela 2020-02-05 às 20 06 28

    opened by PrismaSoftGC 19
  • whenever i select the dropdown value in this code cursor(pointer) move to the upper textfield . How to solve this problem??

    whenever i select the dropdown value in this code cursor(pointer) move to the upper textfield . How to solve this problem??

    Column( children: [

                Padding(
                  padding: const EdgeInsets.only(top: 14),
                  child: new TextField(
    
                    // maxLengthEnforced: true,
                    keyboardType: TextInputType.multiline,
                    controller: controller1,
                    decoration: new InputDecoration(
                      // contentPadding: EdgeInsets.symmetric(vertical: 26,horizontal: 10),
                        border: OutlineInputBorder(),
                        hintText: "Add Task name Only",
                        labelText: "Add Task name only"
                    ),
                  ),
                ),
                new SizedBox(
                    height:6
                ),
    
                Column(
                  children: <Widget>[
    
                    Row(
                      mainAxisAlignment: MainAxisAlignment.start,
                      children: <Widget>[
                        new Text("Select Product:", style: new TextStyle(fontSize: 12),),
                      ],
                    ),
    
                    Container(
    
                      height: 50,
                      width: MediaQuery.of(context).size.width,
                      decoration: ShapeDecoration(
    
                        shape: RoundedRectangleBorder(
    
                          side: BorderSide(width: 1.0, style: BorderStyle.solid, color: Colors.blue),
                          borderRadius: BorderRadius.all(Radius.circular(5.0)),
                        ),
                      ),
                      child: Padding(
                        padding: const EdgeInsets.symmetric(horizontal:10.0),
                        child: StatefulBuilder(
                          //stream: null,
                            builder: (BuildContext context, StateSetter setState) {
                              /*  return  DropdownButton<String>(
                                //  hint: Text('Project'),
                                  value:applicationDropDown,
                                  isExpanded: true,
    
                                  // style: Theme.of(context).textTheme.title,
                                  //  icon: Icon(Icons.arrow_drop_down,color: Colors.lightBlue,),
    
                                  onChanged: (String newValue) {
                                    setState(() {
                                      applicationDropDown = newValue;
                                    });
    
    
                                  },
                                  items: applicationList.cast<String>()
    
                                      .map<DropdownMenuItem<String>>((String value) {
                                    return DropdownMenuItem<String>(
                                      value: value,
                                      child: Text(value),
    
                                    );
                                  })
                                      .toList()
                              );        */
    
                              return SearchableDropdown.single(
                                items: applicationList.cast<String>()
    
                                    .map<DropdownMenuItem<String>>((String value) {
                                  return DropdownMenuItem<String>(
                                    value: value,
                                    child: Text(value, ),
    
                                  );
                                })
                                    .toList(),
                                value: applicationDropDown,
                                hint: "Select one",
                                searchHint: "Select one",
                                onChanged: (value) {
                                  setState(() {
                                    applicationDropDown = value;
                                  });
                                },
                                isExpanded: true,
                                // label: "Select Project",
    
                                selectedValueWidgetFn: (item) {
                                  return Container(
                                      transform: Matrix4.translationValues(-10,0,0),
                                      alignment: Alignment.centerLeft,
                                      child: (Text(item.toString())));
                                },
    
                                // style: Theme.of(context).textTheme.title,
                                displayClearIcon: false,
    
                              );
                            }
                        ),
                      ),
                    ),
                  ],
                ),
    
    
                Padding(
                  padding: const EdgeInsets.only(top:7.0),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.start,
                    children: <Widget>[
                      Column(
                        //  mainAxisAlignment: MainAxisAlignment.start,
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: <Widget>[
                          new Text("Select Customer:", style: new TextStyle(fontSize: 12),),
    
                          Container(
    
                            height: 50,
                            width: MediaQuery.of(context).size.width/2,
                            decoration: ShapeDecoration(
    
                              shape: RoundedRectangleBorder(
    
                                side: BorderSide(width: 1.0, style: BorderStyle.solid, color: Colors.blue),
                                borderRadius: BorderRadius.all(Radius.circular(5.0)),
                              ),
                            ),
                            child: Padding(
                              padding: const EdgeInsets.symmetric(horizontal:10.0),
                              child: StatefulBuilder(
                                //stream: null,
                                  builder: (BuildContext context, StateSetter setState) {
                                    return SearchableDropdown.single(
                                      items: projectList.cast<String>()
    
                                          .map<DropdownMenuItem<String>>((String value) {
                                        return DropdownMenuItem<String>(
                                          value: value,
                                          child: Text(value, ),
    
                                        );
                                      })
                                          .toList(),
                                      value: projectDropDown,
                                      hint: "Select one",
                                      searchHint: "Select one",
                                      onChanged: (value) {
                                        setState(() {
                                          projectDropDown = value;
                                        });
                                      },
                                      isExpanded: true,
                                      // label: "Select Project",
    
                                      selectedValueWidgetFn: (item) {
                                        return Container(
                                            transform: Matrix4.translationValues(-10,0,0),
                                            alignment: Alignment.centerLeft,
                                            child: (Text(item.toString())));
                                      },
    
    
                                      // style: Theme.of(context).textTheme.title,
                                      displayClearIcon: false,
    
                                    );
                                  }
                              ),
                            ),
                          ),
                        ],
    

    in this code whenever i select any dropdown value cursor move to the add task textfiled. Is there any way to solve this problem. SO that cursor do not move to the textfiled again and again.

    will be thankful for your help

    opened by Robinp1011 10
  • Search not working

    Search not working

    I have using searchable_dropdown lib, every thing working but searching option not working properly.I am sharing code with some screenshots, lease check help me.

    Container( padding: EdgeInsets.all(10.0), child: SearchableDropdown.single( items: _dropdownMenuItemsCamp, value: selectCamp, displayClearIcon: false, isCaseSensitiveSearch: true, hint: "Select Compaign Type *", label: Text( "Select Compaign Type *", style: TextStyle(color: Colors.black54), ), searchHint: "Select Compaign Type *", onChanged: (value) { setState(() { selectCamp = value; }); campType = selectCamp.generalID; }, underline: Container( height: 1.0, decoration: BoxDecoration( border: Border( bottom: BorderSide( color: Colors.black38, width: 1.0))), ), searchFn: (String keyword, items) { List ret = List(); if (keyword != null && 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.generate(items.length).toList(); } return (ret); }, isExpanded: true, ), )

    Screenshot_2020-04-11-08-50-56-080_worldjobsnews com flutter_app Screenshot_2020-04-11-08-50-49-921_worldjobsnews com flutter_app

    opened by abhihasabe 7
  • keyboardType, validator, label, searchFn, mutipleSelection + format + version + description

    keyboardType, validator, label, searchFn, mutipleSelection + format + version + description

    Label and error returned by function as Widget:

          validator: (value){return(value==null?Row(
            children: <Widget>[
              Icon(Icons.error,color: Colors.red,size: 14,),
              SizedBox(width: 5,),
              Text("Mandatory",style: TextStyle(color: Colors.red,fontSize: 13),),
            ],
          ):null);},
          label: (value){return(Row(
            children: <Widget>[
              Icon(Icons.info,color: Colors.blueAccent,size: 14,),
              SizedBox(width: 5,),
              Text("Oil producer",style: TextStyle(color: Colors.blueAccent,fontSize: 13),),
            ],
          ));},
    

    image Or as strings:

          validator: (value){return(value==null?"Mandatory":null);},
          label: (value){return("Oil producer");},
    

    image

    Possibility to set the keyboard type for searches as follows:

    keyboardType: TextInputType.number,
    

    image

    New searchFn lets the developer define the search function through which the list items are filtered. For example, the string typed by the user can be considered as keywords separated by spaces:

          searchFn: (String keyword, List<DropdownMenuItem<MyData>> items) {
            List<int> ret = List<int>();
            if (keyword != null && items != null) {
              keyword.split(" ").forEach((k) {
                int i = 0;
                items.forEach((item) {
                  if (keyword.isEmpty || (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);
          },
    

    Here is an example of result:

    image

    Multiple selection

    Multiple selection is available through the multipleSelection parameter:

    multipleSelection: true,
    

    image This disables onChanged callback function. See below.

    There is a way to customize the display of the selected and unselected items through the displayItemWhenMultiple parameter function:

          displayItemWhenMultiple: (item,selected) {
            return (Row(children: [
              selected ? Icon(Icons.check, color: Colors.green,) : Icon(
                Icons.check_box_outline_blank, color: Colors.grey,),
              SizedBox(width: 7),
              item
            ]));
          },
    

    image

    The done button is displayed only when in multiple selection mode. It can be customized either by setting the String parameter doneText or by setting the function parameter doneButtonFn:

    doneText: "OK",
    

    image Or:

          doneButtonFn: (newContext){return FlatButton(
              onPressed: () {
                Navigator.pop(newContext);
              }, child: Text("I'm through"));},
    

    image

    The multiple items set by default can be set through the parameter selectedItems as follows:

      List<int> selectedItems = [1,3];
    ...
          selectedItems: selectedItems,
    

    Where the int values are the indexes of the selected items in the list of items. image

    Once the selection is done, the selectedItems list above is updated or the values can be retrieved through the onChangedMultiple function parameter:

          onChangedMultiple: (List<int> selectedIndexes){
            selectedIndexes.forEach((item){
              print("selected index: $item");
            });
          },
    

    The indexes of the selected items are sent to this callback function. Note that in the multiple selection case, the onChanged callback function is not called as the parameter type is not a list.

    The validator function can also be used in the multiple selection case:

    validator: (List<int>selectedIndexes){return(selectedIndexes==null||selectedIndexes.length==0?"Mandatory":null);},
    

    The selectedValueWidgetFn function can be used in the mutiple selection case the same way it is used in the single selection case:

          selectedValueWidgetFn: (value) => Padding(
              padding: EdgeInsets.fromLTRB(20, 20, 20, 20),
              child:Text(
                value.toString(),
                style: TextStyle(color:Colors.green),
              )
          ),
    

    The clear button works the same way in multiple and single cases.

    Ran:

    flutter format lib/searchable_dropdown.dart
    

    As suggested here: https://pub.dev/packages/searchable_dropdown#-analysis-tab-

    Improved package description as suggested here: https://pub.dev/packages/searchable_dropdown#-analysis-tab- Increased version from 1.1.0 to 1.1.1

    Added to the README.md file:

    • Values of the DropdownMenuItem objects must be strings or implement the toString() method.
    • Clear button.
    • Assert unique value can be avoided.
    • Label
    • Validator
    • Search keyboard
    • Search function
    opened by lcuis 6
  • widget.value == null

    widget.value == null

    I use your demo,i had this problen, family: Roboto, size: 15.0, weight: 400, baseline: ideographic, decoration: TextDecoration.none, softWrap: wrapping at box width, overflow: clip): 'package:searchable_dropdown/searchable_dropdown.dart': Failed assertion: line 90 pos 12: 'widget.value == null || widget.items.where((DropdownMenuItem item) => item.value == widget.value).length == 1': is not true.

    opened by zangen 6
  • Setting a preselected value with

    Setting a preselected value with "value" - selecting other values has no effect

    Hello,

    When I set a preselected value via "value" I am unable to select other values as the selected value is always the preselected one.

                        child: SearchableDropdown.single(
                            value: "USD",
                            items: ["USD", "EUR", "CHF", "HUF"]
    
    opened by giorgio79 4
  • Searchable dropDown value is changing only once in  stateful widget in flutter

    Searchable dropDown value is changing only once in stateful widget in flutter

    Hi, my problem is when i try to select value in searchable dropdown second time then it is not changing remaining same as that in case of ist change , but i can select different value after clearing the value .

    code:

    SearchableDropdown.single( items: widget.projectList.cast()

                                          .map<DropdownMenuItem<String>>((String value) {
                                        return DropdownMenuItem<String>(
                                          value: value,
                                          child: Text(value),
    
                                        );
                                      })
                                          .toList(),
                                      value: dropDownValue,
                                      hint: "Select one",
                                      searchHint: "Select one",
                                      onChanged: (value) {
                                        setState(() {
                                          dropDownValue = value;
                                        });
                                      },
                                      isExpanded: true,
                                      // label: "Select Project",
                                      selectedValueWidgetFn: (item) {
                                        return Container(
                                            transform: Matrix4.translationValues(-10,0,0),
                                            alignment: Alignment.centerLeft,
                                            child: (Text(item.toString())));
                                      },
    
    
                                      // style: Theme.of(context).textTheme.title,
                                   //   displayClearIcon: false,
    
                                    );
    

    and this code is working perfect in stateless widget but problem coming in stateful widget.

    i will be thankful for your solution

    opened by Robinp1011 4
  • Close button label text and optional clear button

    Close button label text and optional clear button

    Hello @icemanbsi ,

    Thanks for searchable_dropdown!

    This pull request proposes 2 things:

    • Close button label text: optionally change the "Close" text of the search dialog for example to allow translation.
    • Clear button: optionally add a clear button [X] on the right of the dropdown as requested by @Laban1 here: https://github.com/icemanbsi/searchable_dropdown/issues/5

    For the clear button:

    • By default, it is not displayed
    • It is active if and only if a value is selected
    • The default icon [X] can be given as a parameter
    • A callback function (onClear) can be given as a parameter and is called when the user clicks on the clear button
    opened by lcuis 4
  • Should be able to implement queryBuilder

    Should be able to implement queryBuilder

    Instead of filter value from DropdownMenuItem, I think it is better to add query builder which allow user to customize any filter list. For example:

            SearchableDropdown(
                  dataList: operators,
                  queryBuilder: (String keyword, List<OperatorModel> list) {
                    List<int> shownIndexes = [];
                    int i = 0;
                    list.forEach((item) {
                      if(keyword.isEmpty || item.opeCode.toLowerCase().contains(keyword.trim().toLowerCase())
                          || item.opeName.toLowerCase().contains(keyword.trim().toLowerCase())
                          || item.opeNameEn.toLowerCase().contains(keyword.trim().toLowerCase())){
                        shownIndexes.add(i);
                      }
                      i++;
                    });
                    return shownIndexes;
                  },
                  items: operators.map((operator) {
                    return DropdownMenuItem<int>(
                      child: Text(operator.opeCode + "-" + operator.opeName),
                      value: operator.opeId
                    ); 
                  }).toList(),
                  value: selectedOperatorId,
                  hint: new Text('Select One'),
                  searchHint: new Text('Select One', style: new TextStyle(fontSize: 20)),
                  onChanged: (value) {
                    setState(() {
                      selectedOperatorId = value;
                    });
                  },
                  isExpanded: true,
                )
    
    opened by mankeomorakort 4
  • error

    error

    /C:/src/flutter/.pub-cache/hosted/pub.dartlang.org/searchable_dropdown-1.1.3/lib/searchable_dropdown.dart:371:41: Error: The getter 'subhead' isn't defined for the class 'TextTheme'.

    • 'TextTheme' is from 'package:flutter/src/material/text_theme.dart' ('/C:/src/flutter/packages/flutter/lib/src/material/text_theme.dart'). Try correcting the name to the name of an existing getter, or defining a getter or field named 'subhead'. ? Theme.of(context).textTheme.subhead ^^^^^^^ /C:/src/flutter/.pub-cache/hosted/pub.dartlang.org/searchable_dropdown-1.1.3/lib/searchable_dropdown.dart:374:16: Error: The getter 'subhead' isn't defined for the class 'TextTheme'.

    • 'TextTheme' is from 'package:flutter/src/material/text_theme.dart' ('/C:/src/flutter/packages/flutter/lib/src/material/text_theme.dart'). Try correcting the name to the name of an existing getter, or defining a getter or field named 'subhead'. .subhead ^^^^^^^ /C:/src/flutter/.pub-cache/hosted/pub.dartlang.org/searchable_dropdown-1.1.3/lib/searchable_dropdown.dart:832:25: Error: The getter 'FlatButton' isn't defined for the class '_DropdownDialogState'.

    • '_DropdownDialogState' is from 'package:searchable_dropdown/searchable_dropdown.dart' ('/C:/src/flutter/.pub-cache/hosted/pub.dartlang.org/searchable_dropdown-1.1.3/lib/searchable_dropdown.dart'). Try correcting the name to the name of an existing getter, or defining a getter or field named 'FlatButton'. return (FlatButton.icon( ^^^^^^^^^^ /C:/src/flutter/.pub-cache/hosted/pub.dartlang.org/searchable_dropdown-1.1.3/lib/searchable_dropdown.dart:993:17: Error: The method 'FlatButton' isn't defined for the class '_DropdownDialogState'.

    • '_DropdownDialogState' is from 'package:searchable_dropdown/searchable_dropdown.dart' ('/C:/src/flutter/.pub-cache/hosted/pub.dartlang.org/searchable_dropdown-1.1.3/lib/searchable_dropdown.dart'). Try correcting the name to the name of an existing method, or defining a method named 'FlatButton'. FlatButton( ^^^^^^^^^^ Error: Cannot run with sound null safety, because the following dependencies don't support null safety:

    • package:searchable_dropdown

    For solutions, see https://dart.dev/go/unsound-null-safety

    opened by gulsenkeskin 3
  • The getter 'subhead' isn't defined for the class 'TextTheme' - Cannot Debug/Run Flutter App

    The getter 'subhead' isn't defined for the class 'TextTheme' - Cannot Debug/Run Flutter App

    ERROR STACK

    ../../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/searchable_dropdown-1.1.3/lib/searchable_dropdown.dart:371:41: Error: The getter 'subhead' isn't defined for the class 'TextTheme'.

    • 'TextTheme' is from 'package:flutter/src/material/text_theme.dart' ('../../../snap/flutter/common/flutter/packages/flutter/lib/src/material/text_theme.dart'). Try correcting the name to the name of an existing getter, or defining a getter or field named 'subhead'. ? Theme.of(context).textTheme.subhead ^^^^^^^

    ../../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/searchable_dropdown-1.1.3/lib/searchable_dropdown.dart:374:16: Error: The getter 'subhead' isn't defined for the class 'TextTheme'.

    • 'TextTheme' is from 'package:flutter/src/material/text_theme.dart' ('../../../snap/flutter/common/flutter/packages/flutter/lib/src/material/text_theme.dart'). Try correcting the name to the name of an existing getter, or defining a getter or field named 'subhead'. .subhead ^^^^^^^ FAILURE: Build failed with an exception.
    • Where: Script '/home/user/snap/flutter/common/flutter/packages/flutter_tools/gradle/flutter.gradle' line: 1005

    • What went wrong: Execution failed for task ':app:compileFlutterBuildDebug'.

    Process 'command '/home/user/snap/flutter/common/flutter/bin/flutter'' finished with non-zero exit value 1

    • Try: Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

    • Get more help at https://help.gradle.org

    BUILD FAILED in 31s Exception: Gradle task assembleDebug failed with exit code 1

    Flutter Doctor Doctor summary (to see all details, run flutter doctor -v): [✓] Flutter (Channel stable, 2.5.1, on Ubuntu 20.04.3 LTS 5.11.0-34-generic, locale en_GB.UTF-8) [!] Android toolchain - develop for Android devices (Android SDK version 30.0.2) ✗ cmdline-tools component is missing Run path/to/sdkmanager --install "cmdline-tools;latest" See https://developer.android.com/studio/command-line for more details. ✗ Android license status unknown. Run flutter doctor --android-licenses to accept the SDK licenses. See https://flutter.dev/docs/get-started/install/linux#android-setup for more details. [✓] Chrome - develop for the web [✓] Android Studio (version 2020.3) [!] Android Studio ✗ android-studio-dir = /snap/android-studio/current/android-studio ✗ Android Studio not found at /snap/android-studio/current/android-studio [✓] VS Code (version 1.60.0) [✓] Connected device (2 available)

    ! Doctor found issues in 2 categories.

    opened by polroti 13
  • Flutter 2.5 compatibility

    Flutter 2.5 compatibility

    fix /usr/local/Caskroom/flutter/2.0.2/flutter/.pub-cache/hosted/pub.dartlang.org/searchable_dropdown-1.1.3/lib/searchable_dropdown.dart:371:41: Error: The getter 'subhead' isn't defined for the class 'TextTheme'. - 'TextTheme' is from 'package:flutter/src/material/text_theme.dart' ('/usr/local/Caskroom/flutter/2.0.2/flutter/packages/flutter/lib/src/material/text_theme.dart'). Try correcting the name to the name of an existing getter, or defining a getter or field named 'subhead'. ? Theme.of(context).textTheme.subhead

    opened by BryanR57 0
  • Need support for search delay (debounce)

    Need support for search delay (debounce)

    Thank you for really helpful flutter package. I think there is an important feature missing which is debounce. When user search in list it immediately invkoing the searchFn which is a performance issue.

    It will be more helpful, if there is an option for setting a property for search delay like debounce:500 or delay:500 where 500 is milliseconds value.

    opened by farukaziz 4
Owner
Bobby Stenly Irawan
Bobby Stenly Irawan
A highly customizable multiple selection widget with search functionality.

A highly customizable multiple selection widget with search functionality.

null 5 Dec 19, 2022
A widget that can be dragged and scrolled in a single gesture and snapped to a list of extents.

Sliding Sheet A widget that can be dragged and scrolled in a single gesture and snapped to a list of extents. Click here to view the full example. Ins

null 396 Mar 10, 2022
A custom dropdown button lets the user select from a number of items

CircularDropDownMenu Description A custom dropdown button lets the user select from a number of items. The button shows the currently selected item as

Surya Dev Singh 2 Dec 5, 2020
A multi select form field using alert dialog to select multiple items with checkboxes and showing as chips.

A multi select form field using alert dialog to select multiple items with checkboxes and showing as chips.

Carlos Eugenio Torres 73 Sep 7, 2022
SKAlertDialog - A highly customizable, powerful and easy-to-use alert dialog for Flutter.

SKAlertDialog A highly customizable, powerful and easy-to-use alert dialog for Flutter. GIF Screenshots SKAlertDialog Basic Alert Alert with buttons A

Senthil_Kumar 7 May 18, 2022
Progress Dialog widget for flutter projects with ability to customize loading widget, background color and background blur.

DISCONTINUED Checkout ArsDialog ars_progress_dialog Customizable progress dialog for Flutter applications with smooth animation for background dim col

Arsam 8 Apr 15, 2022
Animated Search Bar package lets you add a beautiful search bar to your Flutter app.

Animated Search Bar Animated Search Bar package lets you add a beautiful search bar to your Flutter app. Installation Add the latest version of packag

Mohammad Saleh 5 Aug 7, 2022
SmartSelect allows you to easily convert your usual form select or dropdown into dynamic page

SmartSelect allows you to easily convert your usual form select or dropdown into dynamic page, popup dialog, or sliding bottom sheet with various choices input such as radio, checkbox, switch, chips, or even custom input. Supports single and multiple choice.

Irfan Vigma Taufik 332 Dec 20, 2022
A Flutter widget to show a text form field to display a date or clock dialog

A Flutter widget to show a text form field to display a date or clock dialog. This widget extend TextField and has a similar behavior as TextFormField.

m3uzz Soluções em TI 82 Jan 6, 2023
PlutoGrid is a dataGrid that can be controlled by the keyboard on desktop and web. Of course, it works well on Android and IOS.

PlutoGrid is a dataGrid that can be controlled by the keyboard on desktop and web.

Manki Kim 453 Jan 4, 2023
A widget for swiping through a deck of cards with gestures or buttons.

swiping_card_deck A widget for swiping through a deck of cards with gestures or buttons. This package was inspired when I was trying to develop a Tind

Justin Hutchins 8 Oct 17, 2022
Global loading widget, which can be used through simple configuration.

load Global loading widget, which can be used through simple configuration. Pure flutter library, not use native code. It is similar to OKToast in use

Caijinglong 35 Nov 4, 2022
📸 Easy to use yet very customizable zoomable image widget for Flutter, Photo View provides a gesture sensitive zoomable widget.

?? Easy to use yet very customizable zoomable image widget for Flutter, Photo View provides a gesture sensitive zoomable widget. Photo View is largely used to show interacive images and other stuff such as SVG.

Blue Fire 1.7k Jan 7, 2023
A popup simple topModalSheet menu button widget with handsome design and easy to use

top_modal_sheet A popup simple topModalSheet menu button widget with handsome design and easy to use. Installations Add top_modal_sheet: ^1.0.0 in you

Baldemar Alejandres 5 Jul 29, 2022
Similar to Weibo dynamics, WeChat circle of friends, nine grid view controls to display pictures. Support single big picture preview.

Similar to Weibo dynamics, WeChat circle of friends, nine grid view controls to display pictures. Support single big picture preview.

Flutter中国开源项目 296 Dec 28, 2022
A widget that allow user resize the widget with drag

Flutter-Resizable-Widget A widget that allow user resize the widget with drag Note: this widget uses Getx Example bandicam.2021-11-11.12-34-41-056.mp4

MohammadAminZamani.afshar 22 Dec 13, 2022
A widget to provides horizontal or vertical multiple split view for Flutter.

Multi split view A widget to provides horizontal or vertical multiple split view for Flutter. Horizontal or vertical Configurable weight or size for e

Carlos Eduardo Leite de Andrade 63 Dec 28, 2022
Display simple blurry dialog popup for flutter

Blurry Dialog Features Display simple blurry dialog popup Offer built-in themes Possibility to create you custom dialog button click handler callbacks

Kouki Badr 7 Dec 18, 2022
Make your native android Dialog Fancy and Gify.

Make your native android Dialog Fancy and Gify. A library that takes the standard Android Dialog to the next level with a variety of styling options and Gif's. Style your dialog from code.

Shashank Singhal 522 Jan 2, 2023