A highly customizable dropdown package for Flutter with overlay-based rendering, smooth animations, and full control over appearance and behavior — all in a single widget.
- Single Unified Widget: One
FlutterDropdownButton<T>for all use cases - Two Modes: Custom widget rendering (default) or text-only (
.text()) - 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
- 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
![]() |
![]() |
| Basic Text Dropdown Simple text options with customizable styles |
Icon + Text Dropdown Rich content with icons and hover effects |
Add to your pubspec.yaml:
dependencies:
flutter_dropdown_button: ^2.0.0Import the package:
import 'package:flutter_dropdown_button/flutter_dropdown_button.dart';FlutterDropdownButton<String>.text(
items: ['Apple', 'Banana', 'Cherry'],
value: selectedValue,
hint: 'Select a fruit',
width: 200,
onChanged: (value) {
setState(() => selectedValue = value);
},
)FlutterDropdownButton<String>.text(
items: ['Apple', 'Banana', 'Cherry'],
value: selectedValue,
hint: 'Select a fruit',
minWidth: 120,
maxWidth: 300,
onChanged: (value) {
setState(() => selectedValue = value);
},
)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);
},
)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);
},
)The unified dropdown widget. Use the default constructor for custom widget rendering, or .text() for text-only content.
| 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 |
| Parameter | Type | Default | Description |
|---|---|---|---|
items |
List<T> |
required | List of string items |
onChanged |
ValueChanged<T?> |
required | Called when an item is selected |
hint |
String? |
null |
Text shown when no item is selected |
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 |
| Parameter | Type | Default | Description |
|---|---|---|---|
value |
T? |
null |
Currently selected value |
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 |
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 is applied via the theme parameter using DropdownStyleTheme, which groups three theme objects:
DropdownStyleTheme(
dropdown: DropdownTheme(...), // General dropdown styling
scroll: DropdownScrollTheme(...), // Scrollbar styling
tooltip: DropdownTooltipTheme(...), // Tooltip styling
)Controls general styling for button, overlay, and items.
| Parameter | Type | Default | Description |
|---|---|---|---|
buttonDecoration |
BoxDecoration? |
null |
Custom button decoration (overrides all below) |
buttonPadding |
EdgeInsets |
horizontal: 16, vertical: 12 |
Internal padding of button |
buttonHeight |
double? |
null |
Height of button content area (falls back to iconSize or 24.0) |
buttonHoverColor |
Color? |
null |
Button hover background color |
buttonSplashColor |
Color? |
null |
Button tap ripple color |
buttonHighlightColor |
Color? |
null |
Button focus highlight color |
| Parameter | Type | Default | Description |
|---|---|---|---|
overlayDecoration |
BoxDecoration? |
null |
Custom overlay decoration (overrides backgroundColor, border, borderRadius) |
overlayPadding |
EdgeInsets? |
null |
Padding inside the overlay container |
borderRadius |
double |
8.0 |
Border radius for button and overlay |
elevation |
double |
8.0 |
Shadow depth of overlay |
backgroundColor |
Color? |
null |
Overlay background color (falls back to Theme.cardColor) |
border |
Border? |
null |
Border for button and overlay (falls back to Theme.dividerColor) |
shadowColor |
Color? |
null |
Overlay shadow color |
| Parameter | Type | Default | Description |
|---|---|---|---|
itemPadding |
EdgeInsets |
horizontal: 16, vertical: 12 |
Internal padding of each item |
itemMargin |
EdgeInsets? |
null |
External margin around each item |
itemBorderRadius |
double? |
null |
Border radius for individual items |
itemBorder |
Border? |
null |
Border for each item (e.g., bottom divider) |
excludeLastItemBorder |
bool |
true |
Skip itemBorder on the last item |
selectedItemColor |
Color? |
null |
Background color for selected item (falls back to primaryColor 10%) |
itemHoverColor |
Color? |
null |
Item hover background color |
itemSplashColor |
Color? |
null |
Item tap ripple color |
itemHighlightColor |
Color? |
null |
Item focus highlight color |
| 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 |
Controls scrollbar appearance inside the dropdown overlay.
| Parameter | Type | Default | Description |
|---|---|---|---|
thumbWidth |
double? |
null |
Width of the scrollbar thumb |
trackWidth |
double? |
null |
Width of the scrollbar track |
radius |
Radius? |
null |
Corner radius of scrollbar thumb |
thumbColor |
Color? |
null |
Color of the scrollbar thumb |
trackColor |
Color? |
null |
Color of the scrollbar track |
trackBorderColor |
Color? |
null |
Border color of the scrollbar track |
thumbVisibility |
bool? |
null |
Show/hide scrollbar thumb |
trackVisibility |
bool? |
null |
Show/hide scrollbar track |
interactive |
bool? |
null |
Allow dragging the scrollbar thumb |
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) |
Controls tooltip styling and behavior for text-based dropdowns.
| Parameter | Type | Default | Description |
|---|---|---|---|
decoration |
Decoration? |
null |
Custom tooltip decoration |
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 |
borderColor |
Color? |
null |
Tooltip border color |
borderWidth |
double? |
1.0 |
Tooltip border width |
shadow |
List<BoxShadow>? |
null |
Tooltip shadow |
padding |
EdgeInsetsGeometry? |
null |
Padding inside tooltip |
margin |
EdgeInsetsGeometry? |
null |
Margin around tooltip |
| 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 |
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 |
textAlign |
TextAlign |
.start |
Horizontal text alignment |
softWrap |
bool |
true |
Allow line breaks at word boundaries |
FlutterDropdownButton<String>.text(
items: items,
value: selected,
width: 200,
theme: DropdownStyleTheme(
dropdown: DropdownTheme(
borderRadius: 12.0,
elevation: 4.0,
backgroundColor: Colors.white,
selectedItemColor: Colors.blue.withOpacity(0.1),
itemHoverColor: Colors.grey.withOpacity(0.1),
itemBorder: 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),
)FlutterDropdownButton<String>.text(
items: items,
width: 120,
minMenuWidth: 250,
maxMenuWidth: 400,
menuAlignment: MenuAlignment.center,
onChanged: (value) {},
)FlutterDropdownButton<String>.text(
items: ['Only Option'],
disableWhenSingleItem: true,
hideIconWhenSingleItem: true,
onChanged: (value) {},
)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) {},
)Row(
children: [
Text('Label:'),
FlutterDropdownButton<String>.text(
items: items,
expand: true,
maxWidth: 200,
onChanged: (value) {},
),
],
)void navigateToHome() {
FlutterDropdownButton.closeAll();
Navigator.pushNamedAndRemoveUntil(context, '/home', (route) => false);
}Note: Dropdowns are automatically cleaned up during widget disposal. Use closeAll() only when you need explicit control.
// 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) {},
)// 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) {},
)// 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 | Replacement |
|---|---|
DropdownItem<T> |
itemBuilder callback |
showSeparator / separator |
DropdownTheme.itemBorder |
BasicDropdownButton.borderRadius |
DropdownTheme.borderRadius |
BasicDropdownButton.decoration |
DropdownTheme.overlayDecoration |
DropdownItem.onTap |
Handle in onChanged callback |
Check out the example app for a comprehensive demonstration of all features and customization options.
This project is licensed under the MIT License - see the LICENSE file for details.
See CHANGELOG.md for a detailed list of changes and version history.

