multi_dropdown 3.2.2

SDKflutter
Platformandroidioswindowslinuxmacos

Streamlined Flutter widget for versatile multi-selection with extensive customization.

MultiSelect Dropdown

Pub Version License GitHub issues Very Good Analysis Flutter Dart

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
  • ✅ Grouped items with customizable section headers
  • ✅ Select All / Deselect All toggle with customizable labels
  • ✅ Custom search filter for fuzzy matching and multi-field search
  • ✅ Bottom sheet mode for mobile-friendly item selection

Installation

Add to your pubspec.yaml:

dependencies:
  multi_dropdown: ^3.2.1

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...',
  ),
);

With Form Validation

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:

ExampleFileDescription
🌍 Country Pickerbasic_example.dartMulti-select with flag emojis and chip wrapping
🎯 Task Prioritysingle_select_example.dartSingle-select with color-coded result card
👥 Team Memberssearchable_example.dartSearch with custom itemBuilder (avatars)
🏷️ Issue Labelscustom_style_example.dartColor-coded chips, maxDisplayCount, disabled items
🍳 Recipe Ingredientscontroller_example.dartAll MultiSelectController methods
💼 Job Applicationform_validation_example.dartForm validation, maxSelections, submit flow
🌐 Async Loadingfuture_example.dartMultiDropdown.future() with loading spinner
Accessibilityaccessibility_example.dartFont scaling slider, high contrast, Semantics

Run the example:

cd example
flutter run

API Reference

MultiDropdown

ParameterTypeDescriptionDefault
itemsList<DropdownItem<T>>The list of dropdown itemsRequired
singleSelectboolSingle-select modefalse
chipDecorationChipDecorationChip styling configurationChipDecoration()
fieldDecorationFieldDecorationField styling configurationFieldDecoration()
dropdownDecorationDropdownDecorationDropdown panel configurationDropdownDecoration()
searchDecorationSearchFieldDecorationSearch field configurationSearchFieldDecoration()
dropdownItemDecorationDropdownItemDecorationItem styling configurationDropdownItemDecoration()
itemBuilderDropdownItemBuilder<T>?Custom item widget buildernull
selectedItemBuilderSelectedItemBuilder<T>?Custom selected item buildernull
itemSeparatorWidget?Separator between itemsnull
validatorString? Function(...)Form validation callbacknull
autovalidateModeAutovalidateModeWhen to auto-validate.disabled
controllerMultiSelectController<T>?Programmatic controllernull
maxSelectionsintMax selectable items (0 = unlimited)0
enabledboolWhether the dropdown is enabledtrue
searchEnabledboolWhether search is enabledfalse
focusNodeFocusNode?Custom focus nodenull
futureFutureRequest<T>?Async item loadingnull
onSelectionChangeOnSelectionChanged<T>?Selection change callbacknull
onSearchChangeValueChanged<String>?Search text change callbacknull
closeOnBackButtonboolClose on back button pressfalse
ParameterTypeDescriptionDefault
labelStringDisplay labelRequired
valueTAssociated valueRequired
disabledboolWhether item is disabledfalse
selectedboolWhether item is pre-selectedfalse

ChipDecoration

ParameterTypeDescriptionDefault
deleteIconWidget?Chip delete iconIcon(Icons.close)
backgroundColorColor?Chip background colorColor(0xFFE0E0E0)
labelStyleTextStyle?Chip label text stylenull
paddingEdgeInsetsChip paddingEdgeInsets.symmetric(horizontal: 12, vertical: 4)
borderBoxBorderChip borderBorder()
spacingdoubleSpacing between chips8.0
runSpacingdoubleSpacing between chip rows12.0
borderRadiusBorderRadiusGeometryChip border radiusBorderRadius.circular(12)
wrapboolWrap chips or scroll horizontallytrue
maxDisplayCountint?Max visible chips (shows "+N more")null

FieldDecoration

ParameterTypeDescriptionDefault
labelTextString?Label text above the fieldnull
hintTextString?Hint text in the field'Select'
borderInputBorder?Field bordernull
focusedBorderInputBorder?Border when focusednull
disabledBorderInputBorder?Border when disablednull
errorBorderInputBorder?Border on validation errornull
suffixIconWidget?Trailing iconIcon(Icons.arrow_drop_down)
prefixIconWidget?Leading iconnull
labelStyleTextStyle?Label text stylenull
hintStyleTextStyle?Hint text stylenull
borderRadiusdoubleBorder radius12.0
animateSuffixIconboolAnimate suffix icon rotationtrue
paddingEdgeInsets?Content paddingEdgeInsets.symmetric(horizontal: 12, vertical: 8)
backgroundColorColor?Background fill colornull
showClearIconboolShow clear/deselect icontrue
selectedItemTextStyleTextStyle?Selected item text style (single-select)null
inputDecorationInputDecoration?Full InputDecoration overridenull
ParameterTypeDescriptionDefault
backgroundColorColorDropdown background colorColors.white
elevationdoubleDropdown elevation1.0
maxHeightdoubleMaximum dropdown height400.0
borderRadiusBorderRadiusDropdown border radiusBorderRadius.circular(12)
marginTopdoubleGap between field and dropdown0.0
headerWidget?Header widget inside dropdownnull
footerWidget?Footer widget inside dropdownnull
noItemsFoundTextStringText when no items match search'No items found'
expandDirectionExpandDirectionDropdown expand directionExpandDirection.auto

SearchFieldDecoration

ParameterTypeDescriptionDefault
hintTextStringSearch hint text'Search'
borderInputBorder?Search field borderOutlineInputBorder(...)
focusedBorderInputBorder?Border when focusedOutlineInputBorder(...)
searchIconIconSearch iconIcon(Icons.search)
textStyleTextStyle?Search input text stylenull
hintStyleTextStyle?Search hint text stylenull
fillColorColor?Search field fill colornull
filledbool?Whether search field is fillednull
cursorColorColor?Cursor colornull
showClearIconboolShow clear button in searchtrue
autofocusboolAuto-focus search on openfalse
ParameterTypeDescriptionDefault
backgroundColorColor?Item background colornull
disabledBackgroundColorColor?Disabled item background colornull
selectedBackgroundColorColor?Selected item background colornull
selectedTextColorColor?Selected item text colornull
textColorColor?Item text colornull
disabledTextColorColor?Disabled item text colornull
selectedIconWidget?Selected item trailing iconIcon(Icons.check)
disabledIconWidget?Disabled item trailing iconnull
textStyleTextStyle?Item label text stylenull
selectedTextStyleTextStyle?Selected item label text stylenull

Migration from v2.x

See the CHANGELOG for details on breaking changes in v3.0.0.

Key changes:

  • MultiSelectDropDownMultiDropdown
  • ValueItemDropdownItem
  • .network().future()
  • onOptionsSelectedonSelectionChange

License

MIT License