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
Searchable Dropdown : Real-time filtering with customizable search field
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.2.1
Import the package:
import 'package:flutter_dropdown_button/flutter_dropdown_button.dart' ;
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);
},
)
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.
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 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
Common Parameters (Both Modes)
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
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
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 is applied via the theme parameter using DropdownStyleTheme, which groups four theme objects:
DropdownStyleTheme (
dropdown: DropdownTheme (...), // General dropdown styling
scroll: DropdownScrollTheme (...), // Scrollbar styling
tooltip: DropdownTooltipTheme (...), // Tooltip styling
search: SearchFieldTheme (...), // Search field 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
Controls the appearance and behavior of the search text field when searchable is enabled.
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
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
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
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
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 (also controls item alignment in menu)
softWrap
bool
true
Allow line breaks at word boundaries
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),
)
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),
)
Menu Width & Alignment
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) {},
),
],
)
// 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.
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
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.