MultiSelect Dropdown

A powerful and highly customizable multi-select dropdown widget for Flutter. Supports single & multi-select, search, form validation, programmatic control, and extensive visual customization.
Preview

Features
- ✅ Multi-select & single-select modes
- ✅ Searchable dropdown with customizable search field
- ✅ Form validation support with
autovalidateMode
- ✅ Programmatic control via
MultiSelectController
- ✅ Async data loading with
MultiDropdown.future()
- ✅ Extensive decoration classes (chips, field, dropdown, items, search)
- ✅ Pre-selected & disabled items
- ✅ Max selection limit with
maxSelections
- ✅ Chip overflow control with
maxDisplayCount
- ✅ Custom item builders & selected item builders
- ✅ Header & footer widgets in the dropdown
- ✅ Expand direction control (
auto, up, down)
- ✅ Close on back button support
- ✅ Full
InputDecoration override for form consistency
Installation
Add to your pubspec.yaml:
dependencies:
multi_dropdown: ^3.1.0
Quick Start
Basic Multi-Select
MultiDropdown<String>(
items: [
DropdownItem(label: 'Australia', value: 'AU'),
DropdownItem(label: 'Canada', value: 'CA'),
DropdownItem(label: 'India', value: 'IN'),
DropdownItem(label: 'United States', value: 'US'),
],
onSelectionChange: (selectedItems) {
debugPrint('Selected: $selectedItems');
},
);
Single Select
MultiDropdown<String>(
items: items,
singleSelect: true,
fieldDecoration: FieldDecoration(
hintText: 'Choose a role',
suffixIcon: const Icon(Icons.keyboard_arrow_down_rounded),
),
onSelectionChange: (values) {
debugPrint('Selected: ${values.first}');
},
);
Searchable Dropdown
MultiDropdown<String>(
items: items,
searchEnabled: true,
searchDecoration: SearchFieldDecoration(
hintText: 'Type to search...',
),
);
MultiDropdown<String>(
items: items,
maxSelections: 4,
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (selectedItems) {
if (selectedItems == null || selectedItems.isEmpty) {
return 'Please select at least one item';
}
return null;
},
);
Async Data Loading
MultiDropdown<int>.future(
future: () async {
final response = await http.get(Uri.parse('https://api.example.com/users'));
final data = jsonDecode(response.body) as List;
return data.map((e) => DropdownItem(
label: e['name'] as String,
value: e['id'] as int,
)).toList();
},
);
Controller
Use MultiSelectController to programmatically control the dropdown:
final controller = MultiSelectController<String>();
controller.setItems(items); // Set/replace items
controller.addItem(item); // Add a single item
controller.addItems(items); // Add multiple items
controller.selectAll(); // Select all items
controller.clearAll(); // Deselect all items
controller.selectAtIndex(0); // Select item at index
controller.selectWhere((i) => i.value == 'dart'); // Select by predicate
controller.unselectWhere((i) => i.value == 'dart'); // Deselect by predicate
controller.toggleWhere((i) => i.value == 'dart'); // Toggle by predicate
controller.disableWhere((i) => i.value == 'admin'); // Disable by predicate
controller.openDropdown(); // Open the dropdown
controller.closeDropdown(); // Close the dropdown
controller.clearSearch(); // Clear search query
controller.items; // All items
controller.selectedItems; // Selected items
controller.disabledItems; // Disabled items
controller.isOpen; // Dropdown open state
Examples
The example app contains 8 dedicated examples, each in its own file:
Run the example:
cd example
flutter run
API Reference
MultiDropdown
| Parameter |
Type |
Description |
Default |
| items |
List<DropdownItem<T>> |
The list of dropdown items |
Required |
| singleSelect |
bool |
Single-select mode |
false |
| chipDecoration |
ChipDecoration |
Chip styling configuration |
ChipDecoration() |
| fieldDecoration |
FieldDecoration |
Field styling configuration |
FieldDecoration() |
| dropdownDecoration |
DropdownDecoration |
Dropdown panel configuration |
DropdownDecoration() |
| searchDecoration |
SearchFieldDecoration |
Search field configuration |
SearchFieldDecoration() |
| dropdownItemDecoration |
DropdownItemDecoration |
Item styling configuration |
DropdownItemDecoration() |
| itemBuilder |
DropdownItemBuilder<T>? |
Custom item widget builder |
null |
| selectedItemBuilder |
SelectedItemBuilder<T>? |
Custom selected item builder |
null |
| itemSeparator |
Widget? |
Separator between items |
null |
| validator |
String? Function(...) |
Form validation callback |
null |
| autovalidateMode |
AutovalidateMode |
When to auto-validate |
.disabled |
| controller |
MultiSelectController<T>? |
Programmatic controller |
null |
| maxSelections |
int |
Max selectable items (0 = unlimited) |
0 |
| enabled |
bool |
Whether the dropdown is enabled |
true |
| searchEnabled |
bool |
Whether search is enabled |
false |
| focusNode |
FocusNode? |
Custom focus node |
null |
| future |
FutureRequest<T>? |
Async item loading |
null |
| onSelectionChange |
OnSelectionChanged<T>? |
Selection change callback |
null |
| onSearchChange |
ValueChanged<String>? |
Search text change callback |
null |
| closeOnBackButton |
bool |
Close on back button press |
false |
DropdownItem
| Parameter |
Type |
Description |
Default |
| label |
String |
Display label |
Required |
| value |
T |
Associated value |
Required |
| disabled |
bool |
Whether item is disabled |
false |
| selected |
bool |
Whether item is pre-selected |
false |
ChipDecoration
| Parameter |
Type |
Description |
Default |
| deleteIcon |
Widget? |
Chip delete icon |
Icon(Icons.close) |
| backgroundColor |
Color? |
Chip background color |
Color(0xFFE0E0E0) |
| labelStyle |
TextStyle? |
Chip label text style |
null |
| padding |
EdgeInsets |
Chip padding |
EdgeInsets.symmetric(horizontal: 12, vertical: 4) |
| border |
BoxBorder |
Chip border |
Border() |
| spacing |
double |
Spacing between chips |
8.0 |
| runSpacing |
double |
Spacing between chip rows |
12.0 |
| borderRadius |
BorderRadiusGeometry |
Chip border radius |
BorderRadius.circular(12) |
| wrap |
bool |
Wrap chips or scroll horizontally |
true |
| maxDisplayCount |
int? |
Max visible chips (shows "+N more") |
null |
FieldDecoration
| Parameter |
Type |
Description |
Default |
| labelText |
String? |
Label text above the field |
null |
| hintText |
String? |
Hint text in the field |
'Select' |
| border |
InputBorder? |
Field border |
null |
| focusedBorder |
InputBorder? |
Border when focused |
null |
| disabledBorder |
InputBorder? |
Border when disabled |
null |
| errorBorder |
InputBorder? |
Border on validation error |
null |
| suffixIcon |
Widget? |
Trailing icon |
Icon(Icons.arrow_drop_down) |
| prefixIcon |
Widget? |
Leading icon |
null |
| labelStyle |
TextStyle? |
Label text style |
null |
| hintStyle |
TextStyle? |
Hint text style |
null |
| borderRadius |
double |
Border radius |
12.0 |
| animateSuffixIcon |
bool |
Animate suffix icon rotation |
true |
| padding |
EdgeInsets? |
Content padding |
EdgeInsets.symmetric(horizontal: 12, vertical: 8) |
| backgroundColor |
Color? |
Background fill color |
null |
| showClearIcon |
bool |
Show clear/deselect icon |
true |
| selectedItemTextStyle |
TextStyle? |
Selected item text style (single-select) |
null |
| inputDecoration |
InputDecoration? |
Full InputDecoration override |
null |
DropdownDecoration
| Parameter |
Type |
Description |
Default |
| backgroundColor |
Color |
Dropdown background color |
Colors.white |
| elevation |
double |
Dropdown elevation |
1.0 |
| maxHeight |
double |
Maximum dropdown height |
400.0 |
| borderRadius |
BorderRadius |
Dropdown border radius |
BorderRadius.circular(12) |
| marginTop |
double |
Gap between field and dropdown |
0.0 |
| header |
Widget? |
Header widget inside dropdown |
null |
| footer |
Widget? |
Footer widget inside dropdown |
null |
| noItemsFoundText |
String |
Text when no items match search |
'No items found' |
| expandDirection |
ExpandDirection |
Dropdown expand direction |
ExpandDirection.auto |
SearchFieldDecoration
| Parameter |
Type |
Description |
Default |
| hintText |
String |
Search hint text |
'Search' |
| border |
InputBorder? |
Search field border |
OutlineInputBorder(...) |
| focusedBorder |
InputBorder? |
Border when focused |
OutlineInputBorder(...) |
| searchIcon |
Icon |
Search icon |
Icon(Icons.search) |
| textStyle |
TextStyle? |
Search input text style |
null |
| hintStyle |
TextStyle? |
Search hint text style |
null |
| fillColor |
Color? |
Search field fill color |
null |
| filled |
bool? |
Whether search field is filled |
null |
| cursorColor |
Color? |
Cursor color |
null |
| showClearIcon |
bool |
Show clear button in search |
true |
| autofocus |
bool |
Auto-focus search on open |
false |
DropdownItemDecoration
| Parameter |
Type |
Description |
Default |
| backgroundColor |
Color? |
Item background color |
null |
| disabledBackgroundColor |
Color? |
Disabled item background color |
null |
| selectedBackgroundColor |
Color? |
Selected item background color |
null |
| selectedTextColor |
Color? |
Selected item text color |
null |
| textColor |
Color? |
Item text color |
null |
| disabledTextColor |
Color? |
Disabled item text color |
null |
| selectedIcon |
Widget? |
Selected item trailing icon |
Icon(Icons.check) |
| disabledIcon |
Widget? |
Disabled item trailing icon |
null |
| textStyle |
TextStyle? |
Item label text style |
null |
| selectedTextStyle |
TextStyle? |
Selected item label text style |
null |
Migration from v2.x
See the CHANGELOG for details on breaking changes in v3.0.0.
Key changes:
MultiSelectDropDown → MultiDropdown
ValueItem → DropdownItem
.network() → .future()
onOptionsSelected → onSelectionChange
License
MIT License