Flutter Dropdown Button
A highly customizable dropdown package for Flutter with overlay-based rendering, smooth animations, and full control over appearance and behavior.
Features
- Two Widgets:
FlutterDropdownButton<T>chooses one item;FlutterMultiSelectDropdown<T>is a checklist that stays open - Two Modes: Custom widget rendering (default) or text-only (
.text()) - Multi-Select: Tick boxes, get a
Set<T>at once — anchored, not modal, and no confirm button - Any Type in Text Mode: Supply a
labelcallback and keep tooltips, overflow handling and search - Overlay-based Rendering: Better positioning and visual effects than Flutter's built-in DropdownButton
- Smart Positioning: Automatically opens up/down based on available space
- Smooth Animations: Scale and fade effects with configurable timing
- Outside-tap Dismissal: Automatic closure when tapping outside
- Flexible Width: Fixed, min/max constraints, content-based, or flex expansion
- Bare Anchor: Drop the button chrome with
anchorBuilderand embed the menu inside another field,[All ▾] │ search… - Independent Menu Width: Set menu width separately from button with alignment control
- Text Overflow Control: Ellipsis, fade, clip, or visible overflow options
- Smart Tooltip: Automatic tooltip on overflow with full customization
- Custom Scrollbar: Scrollbar theming with colors, thickness, and visibility
- Single-Item Mode: Auto-disable when only one option exists
- Leading Widgets: Optional icons/widgets before text content
- Searchable Dropdown: Real-time filtering with customizable search field
- Build Your Own:
DropdownOverlayControllerexposes the overlay machinery, andTextItemPresentationthe text rendering
Screenshots
![]() |
![]() |
| Basic Text Dropdown Simple text options with customizable styles |
Icon + Text Dropdown Rich content with icons and hover effects |
Quick Start
Add to your pubspec.yaml:
dependencies:
flutter_dropdown_button: ^4.1.0
Import the package:
import 'package:flutter_dropdown_button/flutter_dropdown_button.dart';
Basic Usage
Text Dropdown (Fixed Width)
FlutterDropdownButton<String>.text(
items: ['Apple', 'Banana', 'Cherry'],
value: selectedValue,
hint: 'Select a fruit',
width: 200,
onChanged: (value) {
setState(() => selectedValue = value);
},
)
Text Dropdown (Dynamic Width)
FlutterDropdownButton<String>.text(
items: ['Apple', 'Banana', 'Cherry'],
value: selectedValue,
hint: 'Select a fruit',
minWidth: 120,
maxWidth: 300,
onChanged: (value) {
setState(() => selectedValue = value);
},
)
Text Dropdown of a Domain Type
Pass a label to render any type as text. Overflow handling, the tooltip, and
the default search filter all work off the label.
FlutterDropdownButton<User>.text(
items: users,
value: selectedUser,
label: (user) => user.name,
hint: 'Select a user',
searchable: true, // filters by name, no searchFilter needed
onChanged: (value) {
setState(() => selectedUser = value);
},
)
Reach for the default constructor only when an item needs more than text — an avatar, an icon, a two-line layout.
Custom Widget Dropdown
FlutterDropdownButton<String>(
items: ['home', 'settings', 'profile'],
value: selectedValue,
hintWidget: Text('Choose option'),
itemBuilder: (item, isSelected) => Row(
children: [
Icon(_getIcon(item), size: 20),
SizedBox(width: 8),
Text(item),
],
),
onChanged: (value) {
setState(() => selectedValue = value);
},
)
Custom Selected Display
FlutterDropdownButton<String>(
items: ['apple', 'banana'],
value: selectedValue,
itemBuilder: (item, isSelected) => Text(item),
selectedBuilder: (item) => Text(item.toUpperCase(),
style: TextStyle(fontWeight: FontWeight.bold),
),
onChanged: (value) {
setState(() => selectedValue = value);
},
)
Multi-Select Checklist
Ticking a box calls onChanged immediately with a new Set. The menu stays
open, so the next box can be ticked. There is no confirm button and no scrim.
FlutterMultiSelectDropdown<String>(
items: ['Windows', 'Linux', 'macOS'],
selected: chosen,
labelBuilder: (s) => switch (s.length) {
0 => 'All',
1 => s.first,
_ => '${s.length} selected',
},
searchable: true, // when the list runs long
itemLeadingBuilder: (v) => Icon(osIcon[v]),
itemTrailingBuilder: (v) => Text('${counts[v]}'),
onChanged: (next) => setState(() => chosen = next),
)
selected is yours. The widget copies it before emitting, never edits it, and
draws whatever you hand it — see A value that is not in items.
API Reference
FlutterDropdownButton<T>
The unified dropdown widget. Use the default constructor for custom widget rendering, or .text() for text-only content.
Custom Mode (Default Constructor)
| Parameter | Type | Default | Description |
|---|---|---|---|
items |
List<T> |
required | List of item values |
onChanged |
ValueChanged<T?> |
required | Called when an item is selected |
itemBuilder |
Widget Function(T, bool) |
required | Builds widget for each item (item, isSelected) |
selectedBuilder |
Widget Function(T)? |
null |
Builds widget for selected item on button face (falls back to itemBuilder) |
hintWidget |
Widget? |
null |
Widget shown when no item is selected |
Text Mode (.text Constructor)
| Parameter | Type | Default | Description |
|---|---|---|---|
items |
List<T> |
required | List of item values |
onChanged |
ValueChanged<T?> |
required | Called when an item is selected |
hint |
String? |
null |
Text shown when no item is selected |
label |
String Function(T)? |
null |
Extracts the text to display for an item. Required unless T is String |
config |
TextDropdownConfig? |
null |
Text rendering configuration |
leading |
Widget? |
null |
Widget before text in all items |
selectedLeading |
Widget? |
null |
Widget before text in selected item (falls back to leading) |
leadingPadding |
EdgeInsets? |
right: 8.0 |
Padding around the leading widget |
Common Parameters (Both Modes)
| Parameter | Type | Default | Description |
|---|---|---|---|
value |
T? |
null |
Currently selected value. Drawn on the button whether or not it is in items — see below |
width |
double? |
null |
Fixed width (null = content-based) |
minWidth |
double? |
null |
Minimum width constraint |
maxWidth |
double? |
null |
Maximum width constraint |
height |
double |
200.0 |
Maximum height of dropdown overlay |
itemHeight |
double |
48.0 |
Height of each dropdown item |
animationDuration |
Duration |
200ms |
Duration of show/hide animation |
enabled |
bool |
true |
Whether the dropdown is interactive |
expand |
bool |
false |
Expand to fill available space in flex container |
trailing |
Widget? |
null |
Custom widget replacing default arrow icon |
scrollToSelectedItem |
bool |
true |
Auto-scroll to selected item on open |
scrollToSelectedDuration |
Duration? |
null |
Scroll animation duration (null = instant jump) |
disableWhenSingleItem |
bool |
false |
Disable dropdown when only one item exists |
hideIconWhenSingleItem |
bool |
true |
Hide arrow icon in single-item mode |
minMenuWidth |
double? |
null |
Minimum width of dropdown menu |
maxMenuWidth |
double? |
null |
Maximum width of dropdown menu |
menuAlignment |
MenuAlignment |
.left |
Menu alignment when wider than button |
theme |
DropdownStyleTheme? |
null |
Theme configuration |
searchable |
bool |
false |
Enable search/filter field in dropdown |
searchFilter |
bool Function(T, String)? |
null |
Custom filter function (required for custom mode) |
emptyBuilder |
Widget Function(String)? |
null |
Widget builder for empty search results |
anchorBuilder |
Widget Function(BuildContext, bool isOpen)? |
null |
Draw the anchor yourself, dropping the button chrome (bare mode) — see below |
Bare anchor
Supply anchorBuilder to embed the dropdown inside another field — a field-scope
selector at the head of a search box, [All ▾] │ search… — where the button's
own background, border and fixed width would nest a box inside a box. It drops
that whole button box and hangs the same anchored menu (theming, keyboard
navigation, searchable, itemBuilder) off the widget you return.
The builder is handed isOpen, the one thing it cannot read for itself, so an
inline chevron can turn — AnimatedRotation(turns: isOpen ? 0.5 : 0.0, …). It is
not handed a label; build the face from the value (or selected) you hold.
The button-box params it replaces — width, minWidth, maxWidth, expand,
trailing — must be left unset, and combining them asserts. FlutterMultiSelectDropdown
takes the same parameter.
Because the anchor is compact, the menu — which takes its width from the anchor —
would be compact too. Set minMenuWidth to give the menu a usable width; it
is a menu width, not the button-box width, so it is allowed in bare mode.
A value that is not in items
A list refresh can drop the row a value names while value still names it.
The button keeps drawing that value; the menu, which iterates items, draws no
row for it. The widget draws what it was handed — value is yours, and a
button that silently reverted to its hint would disagree with the state you
hold. Nothing throws.
FlutterMultiSelectDropdown follows the same rule: such a value still counts
towards labelBuilder, so '3 selected' can appear beside two ticked rows.
Give the user a way to clear it.
FlutterMultiSelectDropdown<T>
A checklist. Several items may be chosen, the menu stays open while they are,
and onChanged fires the moment a box is ticked. Anchored rather than modal:
no scrim, dismissed by an outside tap.
Rows render as text, so T must be a String or label must say how to make
one. T must implement == and hashCode consistently — a Set needs
both, where single-select's value == item needed only the first.
| Parameter | Type | Default | Description |
|---|---|---|---|
items |
List<T> |
required | List of item values |
selected |
Set<T> |
required | The chosen items. Yours; never mutated |
onChanged |
ValueChanged<Set<T>> |
required | Called with a new Set the moment a row is tapped |
labelBuilder |
String Function(Set<T>) |
required | Turns selected into the button's face |
label |
String Function(T)? |
null |
The text a row shows. Required unless T is String |
itemLeadingBuilder |
Widget Function(T)? |
null |
Drawn between the checkbox and the label — an icon that varies by item |
itemTrailingBuilder |
Widget Function(T)? |
null |
Drawn at the end of each row — a count, a badge |
config |
TextDropdownConfig? |
null |
Text rendering configuration |
Everything under Common Parameters above applies too, except the five that
mean nothing to a checklist and therefore do not exist on it: value,
scrollToSelectedItem, scrollToSelectedDuration, disableWhenSingleItem and
hideIconWhenSingleItem. They are absent from the type rather than asserted
against at runtime.
FlutterMultiSelectDropdown.closeAll(); // the same registry as the other widget
MenuAlignment
Alignment of the dropdown menu relative to the button when the menu is wider.
| Value | Description |
|---|---|
MenuAlignment.left |
Left edges align, menu extends right (default) |
MenuAlignment.center |
Menu centered over button |
MenuAlignment.right |
Right edges align, menu extends left |
Theme System
Theme is applied via the theme parameter using DropdownStyleTheme, which groups seven theme objects:
DropdownStyleTheme(
button: DropdownButtonTheme(...), // Button face styling
overlay: DropdownOverlayTheme(...), // Menu container styling
item: DropdownItemTheme(...), // Item row styling
scroll: DropdownScrollTheme(...), // Scrollbar styling
tooltip: DropdownTooltipTheme(...), // Tooltip styling
search: SearchFieldTheme(...), // Search field styling
checkbox: DropdownCheckboxTheme(...), // Multi-select row checkbox styling
)
DropdownButtonTheme
Styles the button face — its box, ink colours, content height, and the arrow icon. In bare mode (anchorBuilder) the button box is dropped, so this theme is inert.
resolveButton() returns a style whose slots are all filled, taking a plain DropdownAmbientColors palette rather than a BuildContext — so the rule for "what colour is the arrow when disabled?" lives in one place and can be tested without a widget. You rarely call it directly; the widget does.
Button Styling
| Parameter | Type | Default | Description |
|---|---|---|---|
decoration |
BoxDecoration? |
null |
Custom button decoration (overrides all below) |
backgroundColor |
Color? |
null |
Button fill color |
padding |
EdgeInsets |
horizontal: 16, vertical: 12 |
Internal padding of button |
height |
double? |
null |
Height of button content area (falls back to iconSize or 24.0) |
hoverColor |
Color? |
null |
Button hover background color |
splashColor |
Color? |
null |
Button tap ripple color |
highlightColor |
Color? |
null |
Button focus highlight color |
borderRadius |
double |
8.0 |
Border radius of the button |
border |
Border? |
null |
Border of the button (falls back to Theme.dividerColor) |
Icon Styling
| Parameter | Type | Default | Description |
|---|---|---|---|
icon |
IconData? |
Icons.keyboard_arrow_down |
Dropdown arrow icon |
iconSize |
double? |
24.0 |
Size of the dropdown icon |
iconColor |
Color? |
null |
Icon color when enabled |
iconDisabledColor |
Color? |
null |
Icon color when disabled |
iconPadding |
EdgeInsets? |
EdgeInsets.only(left: 8.0) |
Padding around the icon |
Disabled State
Applied when enabled: false (or when the single-item auto-disable kicks in). If none of these are set, the disabled button falls back to the regular decoration / border.
| Parameter | Type | Default | Description |
|---|---|---|---|
disabledDecoration |
BoxDecoration? |
null |
Full custom decoration for the disabled button (takes precedence over the two below) |
disabledBackgroundColor |
Color? |
null |
Background color of the button when disabled |
disabledBorder |
Border? |
null |
Border of the button when disabled (falls back to border) |
DropdownOverlayTheme
Styles the menu container — its background, border, corner radius, and shadow.
resolveOverlay() returns a fully-filled style from a plain DropdownAmbientColors palette, plus the border thickness the placement module reserves.
| Parameter | Type | Default | Description |
|---|---|---|---|
decoration |
BoxDecoration? |
null |
Custom overlay decoration (overrides backgroundColor, border, borderRadius) |
padding |
EdgeInsets? |
null |
Padding inside the overlay container |
borderRadius |
double |
8.0 |
Border radius of the overlay |
elevation |
double |
8.0 |
Shadow depth of overlay |
backgroundColor |
Color? |
null |
Overlay background color (falls back to Theme.cardColor) |
border |
Border? |
null |
Border of the overlay (falls back to Theme.dividerColor) |
shadowColor |
Color? |
null |
Overlay shadow color |
DropdownItemTheme
Styles the rows inside the menu.
resolveItem() returns a fully-filled style from a plain DropdownAmbientColors palette. The end rows round to meet the menu's corners unless borderRadius overrides.
| Parameter | Type | Default | Description |
|---|---|---|---|
padding |
EdgeInsets |
horizontal: 16, vertical: 12 |
Internal padding of each item |
margin |
EdgeInsets? |
null |
External margin around each item |
borderRadius |
double? |
null |
Border radius for individual items |
border |
Border? |
null |
Border for each item (e.g., bottom divider) |
excludeLastItemBorder |
bool |
true |
Skip border on the last item |
selectedColor |
Color? |
null |
Background color for selected item (falls back to primaryColor 10%) |
hoverColor |
Color? |
null |
Item hover background color |
splashColor |
Color? |
null |
Item tap ripple color |
highlightColor |
Color? |
null |
Item focus highlight color |
DropdownScrollTheme
Controls scrollbar appearance inside the dropdown overlay.
| Parameter | Type | Default | Description |
|---|---|---|---|
thumbWidth |
double? |
null |
Width of the scrollbar. Wins over thickness |
thickness |
double? |
null |
Thickness of the scrollbar. Unset, the ambient ScrollbarTheme decides, then Flutter's own default — 8 on desktop, 4 on Android |
radius |
Radius? |
null |
Corner radius of scrollbar thumb. Unset, Radius.circular(8) on desktop and square on Android |
thumbColor |
Color? |
null |
Color of the scrollbar thumb |
trackColor |
Color? |
null |
Color of the scrollbar track. Paints nothing unless trackVisibility is true |
trackBorderColor |
Color? |
null |
Border color of the scrollbar track, on the same terms as trackColor |
thumbVisibility |
bool? |
null |
true pins the thumb on screen. false and null behave alike: it fades in while scrolling and fades out |
trackVisibility |
bool? |
null |
true draws the track and pins the thumb with it. Unset, there is no track |
interactive |
bool? |
null |
false makes the thumb undraggable. Unset, Flutter decides — draggable on desktop, not on Android |
crossAxisMargin |
double? |
null |
Margin from edge of scroll view |
mainAxisMargin |
double? |
null |
Margin at top/bottom of scrollbar |
minThumbLength |
double? |
null |
Minimum length of scrollbar thumb |
showScrollGradient |
bool? |
false |
Show fade gradient when scrollable |
gradientHeight |
double? |
24.0 |
Height of gradient effect |
gradientColors |
List<Color>? |
null |
Custom gradient colors (auto-detects from background if null) |
A colour is not a request.
trackColordescribes the track;trackVisibilityis what draws it. Anullbool?means let the ambientScrollbarThemedecide — not off.
DropdownTooltipTheme
Controls tooltip styling and behavior for text-based dropdowns.
Visual Styling
| Parameter | Type | Default | Description |
|---|---|---|---|
decoration |
Decoration? |
null |
Custom tooltip decoration; a total override |
backgroundColor |
Color? |
null |
Tooltip background color |
textColor |
Color? |
null |
Tooltip text color |
textStyle |
TextStyle? |
null |
Tooltip text style (overrides textColor) |
borderRadius |
BorderRadius? |
null |
Tooltip corner radius |
border |
BoxBorder? |
null |
Tooltip border |
shadow |
List<BoxShadow>? |
null |
Tooltip shadow |
padding |
EdgeInsetsGeometry? |
null |
Padding inside tooltip |
margin |
EdgeInsetsGeometry? |
null |
Margin around tooltip |
Setting any of backgroundColor, borderRadius, border or shadow hands this
theme the tooltip's whole box; the ones you leave unset fall back to Flutter's own
tooltip defaults. Set none of them and an app-wide TooltipTheme still applies.
Behavior
| Parameter | Type | Default | Description |
|---|---|---|---|
enabled |
bool |
true |
Enable/disable tooltip |
mode |
TooltipMode |
.onlyWhenOverflow |
When to show tooltip |
waitDuration |
Duration |
500ms |
Delay before showing on hover |
showDuration |
Duration |
3s |
How long tooltip stays visible |
verticalOffset |
double? |
null |
Gap between widget and tooltip |
triggerMode |
TooltipTriggerMode? |
null |
How tooltip is triggered |
SearchFieldTheme
Controls the appearance and behavior of the search text field when searchable is enabled.
Layout
| Parameter | Type | Default | Description |
|---|---|---|---|
height |
double? |
36.0 |
Height of the search field |
borderRadius |
BorderRadius? |
circular(8) |
Corner radius of the search field |
margin |
EdgeInsets? |
fromLTRB(8, 8, 8, 4) |
Outer margin around the search field |
padding |
EdgeInsets? |
null |
Inner padding of the search field container |
contentPadding |
EdgeInsets? |
horizontal: 12, vertical: 8 |
Content padding inside the text field |
Styling
| Parameter | Type | Default | Description |
|---|---|---|---|
decoration |
InputDecoration? |
null |
Full InputDecoration override (ignores individual properties when set) |
textStyle |
TextStyle? |
null |
Text style for search input |
backgroundColor |
Color? |
null |
Background color of the search field |
border |
BoxBorder? |
null |
Border when not focused |
focusedBorder |
BoxBorder? |
null |
Border when focused |
divider |
Widget? |
null |
Widget between search field and item list |
Cursor
| Parameter | Type | Default | Description |
|---|---|---|---|
cursorColor |
Color? |
null |
Cursor color |
cursorWidth |
double? |
2.0 |
Cursor width |
cursorHeight |
double? |
null |
Cursor height |
cursorRadius |
Radius? |
null |
Cursor corner radius |
Behavior
| Parameter | Type | Default | Description |
|---|---|---|---|
autofocus |
bool |
true |
Auto-focus search field on dropdown open |
textAlign |
TextAlign |
.start |
Text alignment |
keyboardType |
TextInputType? |
.text |
Keyboard type |
textInputAction |
TextInputAction? |
.search |
Keyboard action button |
DropdownCheckboxTheme
Styles the checkbox on each row of a FlutterMultiSelectDropdown. The box is a FlutterCheckbox (the flutter_checkbox package), reached even though the menu is drawn in the root overlay where a Theme wrapped around the widget cannot. Only the fields that render on the presentational (onChanged: null, semantics-excluded) box are here.
| Parameter | Type | Default | Description |
|---|---|---|---|
activeColor |
Color? |
null |
Fill of a checked box |
checkColor |
Color? |
null |
Color of the checkmark |
inactiveColor |
Color? |
null |
Fill of an unchecked box |
borderColor |
Color? |
null |
Outline of an unchecked box (a checked box's fill covers it) |
borderWidth |
double? |
null (2) |
Width of the unchecked outline |
shape |
CheckboxShape? |
null (rectangle) |
CheckboxShape.rectangle or .circle |
borderRadius |
double? |
null (4) |
Corner radius of a rectangle box |
size |
double? |
null (24) |
Box size in logical pixels |
checkStrokeWidth |
double? |
null (2.5) |
Checkmark stroke width |
checkScale |
double? |
null (1.0) |
Checkmark size within the box |
mouseCursor |
MouseCursor? |
null |
Cursor over the box. Set to SystemMouseCursors.click to match the row's |
A null colour defers to the ambient ThemeData; a null number keeps CheckboxStyle's default (in parentheses). CheckboxShape is exported from this package.
TextDropdownConfig
Configuration for text rendering in .text() mode.
| Parameter | Type | Default | Description |
|---|---|---|---|
overflow |
TextOverflow |
.ellipsis |
How to handle text overflow |
maxLines |
int? |
1 |
Maximum number of lines |
textStyle |
TextStyle? |
null |
Style for item text |
hintStyle |
TextStyle? |
null |
Style for hint text |
selectedTextStyle |
TextStyle? |
null |
Style for selected item text |
disabledTextStyle |
TextStyle? |
null |
Style for button text (value and hint) when disabled — merged over textStyle / hintStyle |
textAlign |
TextAlign |
.start |
Horizontal text alignment (also controls item alignment in menu) |
softWrap |
bool |
true |
Allow line breaks at word boundaries |
Advanced Features
Searchable Dropdown (Text Mode)
FlutterDropdownButton<String>.text(
items: ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry'],
value: selected,
hint: 'Select a fruit',
width: 250,
searchable: true,
onChanged: (value) => setState(() => selected = value),
)
Searchable Dropdown (Custom Mode)
FlutterDropdownButton<User>(
items: users,
value: selectedUser,
searchable: true,
searchFilter: (user, query) =>
user.name.toLowerCase().contains(query.toLowerCase()),
emptyBuilder: (query) => Center(
child: Text('No users matching "$query"'),
),
itemBuilder: (user, isSelected) => Row(
children: [
CircleAvatar(radius: 14, child: Text(user.name[0])),
SizedBox(width: 8),
Text(user.name),
],
),
onChanged: (value) => setState(() => selectedUser = value),
)
Searchable Dropdown with Custom Theme
FlutterDropdownButton<String>.text(
items: countries,
value: selected,
searchable: true,
theme: DropdownStyleTheme(
search: SearchFieldTheme(
backgroundColor: Colors.grey.shade100,
height: 40,
borderRadius: BorderRadius.circular(12),
cursorColor: Colors.blue,
divider: Divider(height: 1),
),
),
onChanged: (value) => setState(() => selected = value),
)
Custom Theme
FlutterDropdownButton<String>.text(
items: items,
value: selected,
width: 200,
theme: DropdownStyleTheme(
overlay: DropdownOverlayTheme(
borderRadius: 12.0,
elevation: 4.0,
backgroundColor: Colors.white,
),
item: DropdownItemTheme(
selectedColor: Colors.blue.withValues(alpha: 0.1),
hoverColor: Colors.grey.withValues(alpha: 0.1),
border: Border(bottom: BorderSide(color: Colors.grey.shade300)),
excludeLastItemBorder: true,
),
scroll: DropdownScrollTheme(
thumbWidth: 6.0,
thumbColor: Colors.blue,
showScrollGradient: true,
),
tooltip: DropdownTooltipTheme(
backgroundColor: Colors.black87,
textStyle: TextStyle(color: Colors.white),
borderRadius: BorderRadius.circular(8),
mode: TooltipMode.onlyWhenOverflow,
),
),
onChanged: (value) => setState(() => selected = value),
)
Menu Width & Alignment
FlutterDropdownButton<String>.text(
items: items,
width: 120,
minMenuWidth: 250,
maxMenuWidth: 400,
menuAlignment: MenuAlignment.center,
onChanged: (value) {},
)
Single-Item Mode
FlutterDropdownButton<String>.text(
items: ['Only Option'],
disableWhenSingleItem: true,
hideIconWhenSingleItem: true,
onChanged: (value) {},
)
Leading Widget
FlutterDropdownButton<String>.text(
items: ['USD', 'EUR', 'JPY'],
leading: Icon(Icons.attach_money, size: 20),
selectedLeading: Icon(Icons.attach_money, size: 20, color: Colors.blue),
leadingPadding: EdgeInsets.only(right: 12),
onChanged: (value) {},
)
Expand in Flex Container
Row(
children: [
Text('Label:'),
FlutterDropdownButton<String>.text(
items: items,
expand: true,
maxWidth: 200,
onChanged: (value) {},
),
],
)
Manual Dropdown Cleanup
// Close with animation (trailing icon rotates back)
FlutterDropdownButton.closeAll();
// Close immediately without animation (useful before navigation)
FlutterDropdownButton.closeAll(animate: false);
Navigator.pushNamedAndRemoveUntil(context, '/home', (route) => false);
Note: Dropdowns are automatically cleaned up during widget disposal. Use closeAll() only when you need explicit control.
Migration from 1.x
BasicDropdownButton → FlutterDropdownButton
// Before (1.x)
BasicDropdownButton<String>(
items: [
DropdownItem(value: 'apple', child: Text('Apple'), onTap: () {}),
DropdownItem(value: 'banana', child: Text('Banana')),
],
value: selected,
hint: Text('Select'),
onChanged: (v) {},
)
// After (2.0)
FlutterDropdownButton<String>(
items: ['apple', 'banana'],
value: selected,
hintWidget: Text('Select'),
itemBuilder: (item, isSelected) => Text(item),
onChanged: (v) {},
)
TextOnlyDropdownButton → FlutterDropdownButton.text
// Before (1.x)
TextOnlyDropdownButton(
items: ['A', 'B', 'C'],
value: selected,
hint: 'Select',
width: 200,
onChanged: (v) {},
)
// After (2.0)
FlutterDropdownButton<String>.text(
items: ['A', 'B', 'C'],
value: selected,
hint: 'Select',
width: 200,
onChanged: (v) {},
)
DynamicTextBaseDropdownButton → FlutterDropdownButton.text
// Before (1.x)
DynamicTextBaseDropdownButton(
items: items,
value: selected,
disableWhenSingleItem: true,
leading: Icon(Icons.star),
onChanged: (v) {},
)
// After (2.0)
FlutterDropdownButton<String>.text(
items: items,
value: selected,
disableWhenSingleItem: true,
leading: Icon(Icons.star),
onChanged: (v) {},
)
Removed APIs
| Removed | Replacement |
|---|---|
DropdownItem<T> |
itemBuilder callback |
showSeparator / separator |
DropdownItemTheme.border |
BasicDropdownButton.borderRadius |
DropdownOverlayTheme.borderRadius |
BasicDropdownButton.decoration |
DropdownOverlayTheme.decoration |
DropdownItem.onTap |
Handle in onChanged callback |
Example
Check out the example app for a comprehensive demonstration of all features and customization options.
Documentation
- Use Cases - Font picker, dynamic width, color picker, country picker examples
- API Reference - Complete API documentation
- Theming - Theming and styling guide
- Text Configuration - Text-specific configuration guide
- Migration - Migration guide from previous versions
License
This project is licensed under the MIT License - see the LICENSE file for details.
Changelog
See CHANGELOG.md for a detailed list of changes and version history.

