Dropdown Flutter
A customizable Flutter dropdown — search, network search, multi-select and form validation built in.
Install
Requires Flutter 3.27+ and Dart 3.6+. The original
custom_dropdown.dart import remains supported.
dependencies:
dropdown_flutter: ^1.3.0
import 'package:dropdown_flutter/dropdown_flutter.dart';
DropdownFlutter<String>(
hintText: 'Select priority',
items: const ['Low', 'Medium', 'High', 'Urgent'],
initialItem: 'Medium', // optional
onChanged: (value) => print(value),
)
That is the whole setup — no builders, controllers or config required.
Constructors
| Constructor | Use it for |
|---|---|
DropdownFlutter() |
A plain list of items |
.search() |
Filtering a local list as the user types |
.searchRequest() |
Fetching results from an API |
.multiSelect() |
Selecting several items with checkboxes |
.multiSelectSearch() |
Multi-select over a filtered local list |
.multiSelectSearchRequest() |
Multi-select over API results |
Choose the constructor for your data source and selection mode. Search options
apply to search constructors; select-all and list validation apply to multi-select.
The multiSelect* variants report through onListChanged; the rest use
onChanged.
Screenshots
| Multi-select | Grouped | Highlighting | Dark theme |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
Usage
Search — local and network
Plain String items search out of the box. For your own type, mix in
CustomDropdownListFilter and decide what counts as a match.
class Member with CustomDropdownListFilter {
const Member(this.name, this.role);
final String name;
final String role;
// toString() supplies the row label; filter() decides what matches.
@override
String toString() => name;
@override
bool filter(String q) => name.toLowerCase().contains(q.toLowerCase()) ||
role.toLowerCase().contains(q.toLowerCase()); // "engineer" finds them all
}
DropdownFlutter<Member>.search(items: members, onChanged: print);
DropdownFlutter<Member>.searchRequest( // same, but from an API
futureRequest: (query) async => api.searchMembers(query),
futureRequestDelay: const Duration(milliseconds: 300),
onChanged: print,
);
Validation and controllers
final controller = SingleSelectController<String?>('Medium');
// MultiSelectController<String>(['Medium']) for multi-select
DropdownFlutter<String>(
items: priorities,
controller: controller, // read and set from anywhere
validator: (value) => value == null ? 'Required' : null,
validateOnChange: true, // listValidator for multi-select
onChanged: print,
);
controller.value = 'High';
controller.clear();
Custom rows
toString() gives the default label. For anything richer, supply a
listItemBuilder — headerBuilder, hintBuilder and noResultFoundBuilder
work the same way.
DropdownFlutter<Member>(
items: members,
listItemBuilder: (context, item, isSelected, onItemSelect) => Row(
children: [
CircleAvatar(child: Text(item.name[0])),
const SizedBox(width: 12),
Text(item.name),
],
),
onChanged: print,
)
Modern UX
The features below are opt-in. Default surfaces and text follow the app theme;
fields support Tab focus and Enter/Space activation. Escape dismisses a menu and
returns focus to the field. Multi-select menus include a selection count and
Done button by default; choices are applied immediately, and Done closes the menu.
Use doneText to localize its label or showMultiSelectFooter: false to hide it.
| Property | Effect |
|---|---|
groupBy |
Splits the list into labelled sections |
highlightMatchedText |
Emphasises the matched substring in results |
recentSelectionsMaxCount |
Pins recently picked items to the top |
showSelectAll |
Adds a select-all / clear-all row (multi-select) |
selectAllText / clearAllText |
Relabel that row |
enableKeyboardNavigation |
Arrow keys move, Enter selects, Escape closes |
enableHapticFeedback |
Light impact on open, click on select |
animationDuration / animationCurve |
Tunes the open/close animation |
DropdownFlutter<Member>(
items: members,
groupBy: (member) => member.team, // any combination works
recentSelectionsMaxCount: 3,
enableKeyboardNavigation: true,
onChanged: print,
)
Styling
decoration covers colors, borders, shadows and text styles; the builders
replace widgets outright. Colors fall back to the ambient ColorScheme, so
dropdowns follow a dark theme with no extra configuration.
DropdownFlutter<String>(
items: items,
decoration: CustomDropdownDecoration(
closedFillColor: const Color(0xFF1E1B33),
closedBorderRadius: BorderRadius.circular(16),
headerStyle: const TextStyle(color: Colors.white),
),
onChanged: print,
)
final withIcon = base.copyWith(prefixIcon: const Icon(Icons.person));
Size is controlled by overlayHeight, listItemPadding and listItemHeight
— the last defaults to null so rows fit their content; setting it lets the list
scroll more efficiently. Menus also respect available viewport space and keyboard
insets, including short lists with tall custom rows.
State and lifecycle
- Create controllers once in
State, and dispose them in yourdispose()method. The dropdown only disposes controllers it creates itself. Controllers may be replaced or removed during rebuilds. initialItem/initialItemsseed an uncontrolled dropdown. Changing these properties resets its selection; with a controller, setcontroller.value. Do not supply both a controller and initial selection.- Multi-select values are immutable, duplicate-free snapshots. Use
add,remove,clear, or assign a new list tovalue; do not mutatecontroller.value. - Local initial selections must all belong to
items. Custom objects should implement consistent==andhashCode. Replace item lists when data changes. If a selected item is removed, clear/update the controller explicitly. FormState.reset()restores the initial selection (or the value present when a controller was attached) and clears errors. Disabled fields do not validate.- Select-all acts on the current filtered/fetched results, preserving selections outside those results. Clear-all removes only the current results.
- Network search runs for nonempty queries. Clearing cancels pending debounce
timers and restores the supplied seed
items. Old requests may finish, but cannot overwrite newer results or update a disposed dropdown. Before typing, an empty remote menu showssearchHintTextinstead of a no-results message. Failed requests show a Retry action that repeats the current query. Customize or localize this state withsearchRequestErrorBuilder(context, error, retry); raw exception details are not displayed by the default UI. closeDropDownOnClearFilterSearchcloses the menu when its clear button is used. Reduced-motion settings skip the menu animation.- Builder callbacks have public types such as
DropdownListItemBuilder<T>andDropdownHeaderBuilder<T>for reusable builders.
Development
flutter pub get
flutter analyze
flutter test
cd example
flutter test
Run the package regression/UX suites and example gallery together on an Android
emulator (from example/):
flutter emulators --launch <emulator-id>
flutter test integration_test/dropdown_flutter_test.dart -d <device-id>
Use flutter emulators and flutter devices to find the IDs.
CI checks the minimum supported Flutter version and stable Flutter.
All properties
| Group | Properties |
|---|---|
| Items | items, initialItem / initialItems, excludeSelected, and the onChanged / onListChanged callbacks |
| Text | hintText, searchHintText, noResultFoundText, maxlines (line limit on the closed header) |
| Sizing | listItemHeight, overlayHeight, listItemPadding / itemsListPadding, closedHeaderPadding / expandedHeaderPadding |
| Behaviour | enabled, canCloseOutsideBounds, hideSelectedFieldWhenExpanded, closeDropDownOnClearFilterSearch, visibility |
| Control | controller / multiSelectController, overlayController, itemsScrollController |
| Async | futureRequest / futureRequestDelay, searchRequestLoadingIndicator, searchRequestErrorBuilder |
| Completion | showMultiSelectFooter, doneText |
| Recents | initialRecentItems, onRecentItemsChanged |
| Validation | validator / listValidator, validateOnChange |
| Appearance | decoration / disabledDecoration |
| Builders | listItemBuilder, headerBuilder / headerListBuilder, hintBuilder, noResultFoundBuilder, groupHeaderBuilder |
Libraries
- custom_dropdown
- Searchable, theme-aware single and multi-select dropdown widgets.
- dropdown_flutter
- Customizable single and multi-select dropdowns with local and async search.



