Skip to content

Repository files navigation

Flutter DropdownButton2

Pub VersionFlutter VersionLicense: MITBuild Status

Intro

Flutter's core Dropdown Button widget with a steady dropdown menu and many other options you can customize to your needs.

Image

Features

  • Dropdown menu always open below the button "as long as it's possible otherwise it'll open to the end of the screen" and you can edit its position by using the offset parameter.
  • You can control how (button, button's icon, dropdown menu and menu items) will be displayed "read Options below".
  • You can align (hint & value) and customize them.
  • You can edit the scrollbar's radius,thickness and isAlwaysShow.
  • You can set max height for the dropdown menu & it'll become scrollable if there are more items.
  • If you pass Null to dropdownMaxHeight parameter or didn't use it, the dropdown menu will take max height possible for the items and will become scrollable if there are more items.
  • If you have long scrollable list, the dropdown menu will auto scroll to current selected item and show it at the middle of the menu if possible.
  • Wrap DropdownButton2 with DropdownButtonHideUnderline to hide the underline.
  • A Custom widget of the DropdownButton2 below to make it more reusable. You can customize it to your needs and use it throughout all your app easily as shown in the examples.
  • You can use DropdownButton2 with items of different heights like dividers as shown in the examples.
  • You can use DropdownButton2 as Multiselect Dropdown with Checkboxes as shown in the examples.
  • You can use DropdownButton2 as Searchable Dropdown as shown in the examples.
  • You can use DropdownButton2 as a popup menu button by using the parameter customButton. You can pass Icon,Image or any widget and customize it as shown in the examples.
  • You can also use DropdownButtonFormField2 the same way with all options above and use it inside Form as shown in the examples.
  • Use decoration parameter for the DropdownButtonFormField2 to add borders, label and more.
  • You can customize DropdownButtonFormField2 width by wrapping it with Padding or with SizedBox and give it the width you want.

Options

DropdownButton2:

OptionDescriptionTypeRequired
itemsThe list of items the user can selectList<DropdownItem>Yes
selectedItemBuilderA builder to customize how the selected item will be displayed on the buttonDropdownButton2BuilderNo
valueListenableA [ValueListenable] that represents the value of the currently selected [DropdownItem].ValueListenable<T?>?No
multiValueListenableA [ValueListenable] that represents a list of the currently selected [DropdownItem]sValueListenable<List<T>>?No
hintThe placeholder displayed before the user choose an itemWidgetNo
disabledHintThe placeholder displayed if the dropdown is disabledWidgetNo
onChangedCalled when the user selects an itemValueChanged<T?>No
onMenuStateChangeCalled when the dropdown menu opens or closesOnMenuStateChangeFnNo
styleThe text style to use for text in the dropdown button and the dropdown menuTextStyleNo
underlineThe widget to use for drawing the drop-down button's underlineWidgetNo
isDenseReduce the button's heightboolNo
isExpandedMakes the button's inner contents expanded (set true to avoid long text overflowing)boolNo
focusNodeAn optional focus node to use as the focus node for the buttonFocusNodeNo
autofocusWhether the button should be focused when first builtboolNo
enableFeedbackWhether detected gestures should provide acoustic and/or haptic feedbackboolNo
mouseCursorThe cursor for a mouse pointer when it enters or is hovering over the buttonMouseCursorNo
alignmentDefines how the hint or the selected item is positioned within the buttonAlignmentGeometryNo
buttonStyleDataUsed to configure the theme of the buttonButtonStyleDataNo
iconStyleDataUsed to configure the theme of the button's iconIconStyleDataNo
dropdownStyleDataUsed to configure the theme of the dropdown menuDropdownStyleDataNo
menuItemStyleDataUsed to configure the theme of the dropdown menu itemsMenuItemStyleDataNo
dropdownSearchDataUsed to configure searchable dropdownsDropdownSearchDataNo
dropdownSeparatorAdds separator widget to the dropdown menuDropdownSeparatorNo
customButtonUses custom widget like icon,image,etc.. instead of the default buttonWidgetNo
openWithLongPressOpens the dropdown menu on long-pressing instead of tappingboolNo
barrierDismissibleWhether you can dismiss this route by tapping the modal barrierboolNo
barrierColorThe color to use for the modal barrier. If this is null, the barrier will be transparentColorNo
barrierLabelThe semantic label used for a dismissible barrierStringNo
barrierCoversButtonSpecifies whether the modal barrier should cover the dropdown button or not.boolNo
barrierBlocksInteractionWhether to block interaction with underlying widgets when the dropdown menu is openboolNo
openDropdownListenableA [Listenable] that can be used to programmatically open the dropdown menu.Listenable?No

Subclass ButtonStyleData:

OptionDescriptionTypeRequired
heightThe height of the buttondoubleNo
widthThe width of the buttondoubleNo
paddingThe inner padding of the ButtonEdgeInsetsGeometryNo
decorationThe decoration of the ButtonBoxDecorationNo
foregroundDecorationThe decoration to paint in front of the ButtonBoxDecorationNo
elevationThe elevation of the ButtonintNo
overlayColorDefines the ink response focus, hover, and splash colors for the buttonWidgetStateProperty<Color?>No

Subclass IconStyleData:

OptionDescriptionTypeRequired
iconThe widget to use for the drop-down button's suffix iconWidgetNo
iconDisabledColorThe color of the icon if the button is disabledColorNo
iconEnabledColorThe color of the icon if the button is enabledColorNo
iconSizeThe size of the icondoubleNo
openMenuIconShows different icon when dropdown menu is openWidgetNo

Subclass DropdownStyleData:

OptionDescriptionTypeRequired
maxHeightThe maximum height of the dropdown menudoubleNo
anchoredMinHeightThe minimum height for the dropdown menu to remain anchored to the buttondoubleNo
widthThe width of the dropdown menudoubleNo
paddingThe inner padding of the dropdown menuEdgeInsetsGeometryNo
scrollPaddingThe inner padding of the dropdown menu including the scrollbarEdgeInsetsGeometryNo
decorationThe decoration of the dropdown menuBoxDecorationNo
elevationThe elevation of the dropdown menuintNo
directionThe direction of the dropdown menu in relation to the buttonDropdownDirectionNo
offsetChanges the position of the dropdown menuOffsetNo
isOverButtonOpens the dropdown menu over the button instead of below itboolNo
useSafeAreaDetermine if the dropdown menu should only display in safe areas of the screenboolNo
useRootNavigatorDetermine whether to open the dropdown menu using the root Navigator or notboolNo
scrollbarThemeConfigures the theme of the menu's scrollbarScrollbarThemeDataNo
openIntervalThe animation curve used for opening the dropdown menu (forward direction)IntervalNo
dropdownBuilderA builder to customize the dropdown menuDropdownBuilderNo

Subclass MenuItemStyleData:

OptionDescriptionTypeRequired
paddingThe padding of menu itemsEdgeInsetsGeometryNo
useDecorationHorizontalPaddingDetermine whether to use the horizontal padding from "decoration.contentPadding" for menu items when using DropdownButtonFormField2boolNo
borderRadiusThe border radius of the menu itemBorderRadiusNo
overlayColorDefines the ink response focus, hover, and splash colors for the itemsWidgetStateProperty<Color?>No
mouseCursorThe cursor for a mouse pointer when it enters or is hovering over a menu itemMouseCursorNo
selectedMenuItemBuilderA builder to customize the selected menu itemSelectedMenuItemBuilderNo

Subclass DropdownSearchData:

OptionDescriptionTypeRequired
searchControllerThe controller used for searchable dropdowns, if null, then it'll perform as a normal dropdownTextEditingControllerNo
searchBarWidgetThe widget to be shown at the top of the dropdown menu for searchable dropdownsWidgetNo
searchBarWidgetHeightThe height of the searchBarWidget if useddoubleNo
noResultsWidgetThe widget to show when the search results are emptyWidgetNo
searchMatchFnThe match function used for searchable dropdowns, if null, defaultFn will be usedSearchMatchFnNo

DropdownButtonFormField2 (In addition to the above):

OptionDescriptionTypeRequired
decorationThe decoration of the dropdown button form fieldInputDecorationNo
onSavedCalled with the current selected item when the form is savedFormFieldSetterNo
validatorCalled to validates if the input is invalid and display error textFormFieldValidatorNo
errorBuilderCalled to display a custom error widget instead of the default error textWidget Function(BuildContext, String)No
forceErrorTextForces the FormFieldState into an error state and displays the given textStringNo
autovalidateModeUsed to enable/disable auto validationAutovalidateModeNo

Installation

add this line to pubspec.yaml

dependencies:
dropdown_button2: ^3.1.0

import package

import'package:dropdown_button2/dropdown_button2.dart';

Usage and Examples

1. Simple DropdownButton2 with no styling:

Image

finalList<String> items = [
'Item1',
'Item2',
'Item3',
'Item4',
];
final valueListenable =ValueNotifier<String?>(null);
@overrideWidgetbuild(BuildContext context) {
returnScaffold(
body:Center(
child:DropdownButtonHideUnderline(
child:DropdownButton2<String>(
isExpanded:true,
hint:Text(
'Select Item',
style:TextStyle(
fontSize:14,
color:Theme.of(context).hintColor,
),
),
items: items
.map(
(String item) =>DropdownItem<String>(
value: item,
height:40,
child:Text(
item,
style:constTextStyle(
fontSize:14,
),
),
),
)
.toList(),
valueListenable: valueListenable,
onChanged: (String? value) {
valueListenable.value = value;
},
buttonStyleData:constButtonStyleData(
padding:EdgeInsets.symmetric(horizontal:16),
height:40,
width:140,
),
),
),
),
);
}

2. DropdownButton2 with few styling and customization:

Image

finalList<String> items = [
'Item1',
'Item2',
'Item3',
'Item4',
'Item5',
'Item6',
'Item7',
'Item8',
];
final valueListenable =ValueNotifier<String?>(null);
@overrideWidgetbuild(BuildContext context) {
returnScaffold(
body:Center(
child:DropdownButtonHideUnderline(
child:DropdownButton2<String>(
isExpanded:true,
hint:constRow(
children: [
Icon(
Icons.list,
size:16,
color:Colors.yellow,
),
SizedBox(
width:4,
),
Expanded(
child:Text(
'Select Item',
style:TextStyle(
fontSize:14,
fontWeight:FontWeight.bold,
color:Colors.yellow,
),
overflow:TextOverflow.ellipsis,
),
),
],
),
items: items
.map(
(String item) =>DropdownItem<String>(
value: item,
height:40,
child:Text(
item,
style:constTextStyle(
fontSize:14,
fontWeight:FontWeight.bold,
color:Colors.white,
),
overflow:TextOverflow.ellipsis,
),
),
)
.toList(),
valueListenable: valueListenable,
onChanged: (value) {
valueListenable.value = value;
},
buttonStyleData:ButtonStyleData(
height:50,
width:160,
padding:constEdgeInsets.only(left:14, right:14),
decoration:BoxDecoration(
borderRadius:BorderRadius.circular(14),
border:Border.all(
color:Colors.black26,
),
color:Colors.redAccent,
),
elevation:2,
),
iconStyleData:constIconStyleData(
icon:Icon(
Icons.arrow_forward_ios_outlined,
),
iconSize:14,
iconEnabledColor:Colors.yellow,
iconDisabledColor:Colors.grey,
),
dropdownStyleData:DropdownStyleData(
maxHeight:200,
width:200,
decoration:BoxDecoration(
borderRadius:BorderRadius.circular(14),
color:Colors.redAccent,
),
offset:constOffset(-20, 0),
scrollbarTheme:ScrollbarThemeData(
radius:constRadius.circular(40),
thickness:WidgetStateProperty.all(6),
thumbVisibility:WidgetStateProperty.all(true),
),
),
menuItemStyleData:constMenuItemStyleData(
padding:EdgeInsets.only(left:14, right:14),
),
),
),
),
);
}

3. DropdownButton2 with separator widgets like dividers:

Image

finalList<String> items = [
'Item1',
'Item2',
'Item3',
'Item4',
];
final valueListenable =ValueNotifier<String?>(null);
@overrideWidgetbuild(BuildContext context) {
returnScaffold(
body:Center(
child:DropdownButtonHideUnderline(
child:DropdownButton2<String>(
isExpanded:true,
hint:Text(
'Select Item',
style:TextStyle(
fontSize:14,
color:Theme.of(context).hintColor,
),
),
items: items
.map(
(String item) =>DropdownItem<String>(
value: item,
height:40,
child:Padding(
padding:constEdgeInsets.symmetric(horizontal:8.0),
child:Text(
item,
style:constTextStyle(
fontSize:14,
),
),
),
),
)
.toList(),
dropdownSeparator:constDropdownSeparator(
height:4,
child:Padding(
padding:EdgeInsets.symmetric(horizontal:8.0),
child:Divider(),
),
),
valueListenable: valueListenable,
onChanged: (value) {
valueListenable.value = value;
},
buttonStyleData:constButtonStyleData(
padding:EdgeInsets.symmetric(horizontal:16),
height:40,
width:140,
),
dropdownStyleData:constDropdownStyleData(
maxHeight:200,
),
menuItemStyleData:constMenuItemStyleData(
padding:EdgeInsets.symmetric(horizontal:8.0),
),
iconStyleData:constIconStyleData(
openMenuIcon:Icon(Icons.arrow_drop_up),
),
),
),
),
);
}

4. DropdownButton2 as Multiselect Dropdown with Checkboxes:

Image

finalList<String> items = [
'All',
'Item1',
'Item2',
'Item3',
'Item4',
];
final multiValueListenable =ValueNotifier<List<String>>([]);
@overrideWidgetbuild(BuildContext context) {
returnScaffold(
body:Center(
child:DropdownButtonHideUnderline(
child:DropdownButton2<String>(
isExpanded:true,
hint:Text(
'Select Items',
style:TextStyle(
fontSize:14,
color:Theme.of(context).hintColor,
),
),
items: items.map((item) {
returnDropdownItem(
value: item,
height:40,
closeOnTap:false,
child:ValueListenableBuilder<List<String>>(
valueListenable: multiValueListenable,
builder: (context, multiValue, _) {
final isSelected = multiValue.contains(item);
returnContainer(
height:double.infinity,
padding:constEdgeInsets.symmetric(horizontal:16.0),
child:Row(
children: [
if (isSelected)
constIcon(Icons.check_box_outlined)
elseconstIcon(Icons.check_box_outline_blank),
constSizedBox(width:16),
Expanded(
child:Text(
item,
style:constTextStyle(
fontSize:14,
),
),
),
],
),
);
},
),
);
}).toList(),
multiValueListenable: multiValueListenable,
onChanged: (value) {
final multiValue = multiValueListenable.value;
final isSelected = multiValue.contains(value);
if (value =='All') {
isSelected
? multiValueListenable.value = []
: multiValueListenable.value =List.from(items);
} else {
multiValueListenable.value = isSelected
? ([...multiValue]..remove(value))
: [...multiValue, value!];
}
},
selectedItemBuilder: (context) {
return items.map(
(item) {
returnValueListenableBuilder<List<String>>(
valueListenable: multiValueListenable,
builder: (context, multiValue, _) {
returnContainer(
alignment:AlignmentDirectional.center,
child:Text(
multiValue.where((item) => item !='All').join(', '),
style:constTextStyle(
fontSize:14,
overflow:TextOverflow.ellipsis,
),
maxLines:1,
),
);
},
);
},
).toList();
},
buttonStyleData:constButtonStyleData(
padding:EdgeInsets.only(left:16, right:8),
height:40,
width:140,
),
menuItemStyleData:constMenuItemStyleData(
padding:EdgeInsets.zero,
),
),
),
),
);
}

5. DropdownButton2 as Searchable Dropdown:

Image

finalList<String> items = [
'A_Item1',
'A_Item2',
'A_Item3',
'A_Item4',
'B_Item1',
'B_Item2',
'B_Item3',
'B_Item4',
];
final valueListenable =ValueNotifier<String?>(null);
finalTextEditingController textEditingController =TextEditingController();
@overridevoiddispose() {
textEditingController.dispose();
super.dispose();
}
@overrideWidgetbuild(BuildContext context) {
returnScaffold(
body:Center(
child:DropdownButtonHideUnderline(
child:DropdownButton2<String>(
isExpanded:true,
hint:Text(
'Select Item',
style:TextStyle(
fontSize:14,
color:Theme.of(context).hintColor,
),
),
items: items
.map(
(item) =>DropdownItem(
value: item,
height:40,
child:Text(
item,
style:constTextStyle(
fontSize:14,
),
),
),
)
.toList(),
valueListenable: valueListenable,
onChanged: (value) {
valueListenable.value = value;
},
buttonStyleData:constButtonStyleData(
padding:EdgeInsets.symmetric(horizontal:16),
height:40,
width:200,
),
dropdownStyleData:constDropdownStyleData(
maxHeight:200,
),
dropdownSearchData:DropdownSearchData(
searchController: textEditingController,
searchBarWidgetHeight:50,
searchBarWidget:Container(
height:50,
padding:constEdgeInsets.only(
top:8,
bottom:4,
right:8,
left:8,
),
child:TextFormField(
expands:true,
maxLines:null,
controller: textEditingController,
decoration:InputDecoration(
isDense:true,
contentPadding:constEdgeInsets.symmetric(
horizontal:10,
vertical:8,
),
hintText:'Search for an item...',
hintStyle:constTextStyle(fontSize:12),
border:OutlineInputBorder(
borderRadius:BorderRadius.circular(8),
gapPadding:0,
),
),
),
),
noResultsWidget:constPadding(
padding:EdgeInsets.all(8),
child:Text('No Item Found!'),
),
searchMatchFn: (item, searchValue) {
return item.value.toString().contains(searchValue);
},
),
//This to clear the search value when you close the menu
onMenuStateChange: (isOpen) {
if (!isOpen && mounted) {
textEditingController.clear();
}
},
),
),
),
);
}

6. DropdownButton2 as Popup menu button using customButton parameter:

Example 1 using icon:

Image

classPopupIconExampleextendsStatefulWidget {
constPopupIconExample({super.key});
@overrideState<PopupIconExample> createState() =>_PopupIconExampleState();
}
class_PopupIconExampleStateextendsState<PopupIconExample> {
@overrideWidgetbuild(BuildContext context) {
returnScaffold(
body:Center(
child:DropdownButtonHideUnderline(
child:DropdownButton2(
customButton:constIcon(
Icons.list,
size:46,
color:Colors.red,
),
items: [
..._MenuItems.firstItems.map(
(item) =>DropdownItem<_MenuItem>(
value: item,
height:48,
child:_MenuItems.buildItem(item),
),
),
constDropdownItem<Divider>(
enabled:false,
height:8,
child:Divider(),
),
..._MenuItems.secondItems.map(
(item) =>DropdownItem<_MenuItem>(
value: item,
height:48,
child:_MenuItems.buildItem(item),
),
),
],
onChanged: (value) {
_MenuItems.onChanged(context, value!as_MenuItem);
},
dropdownStyleData:DropdownStyleData(
width:160,
padding:constEdgeInsets.symmetric(vertical:6),
decoration:BoxDecoration(
borderRadius:BorderRadius.circular(4),
color:Colors.redAccent,
),
offset:constOffset(0, 8),
),
menuItemStyleData:constMenuItemStyleData(
padding:EdgeInsets.only(left:16, right:16),
),
),
),
),
);
}
}
class_MenuItem {
const_MenuItem({
requiredthis.text,
requiredthis.icon,
});
finalString text;
finalIconData icon;
}
abstractclass_MenuItems {
staticconstList<_MenuItem> firstItems = [home, share, settings];
staticconstList<_MenuItem> secondItems = [logout];
staticconst home =_MenuItem(text:'Home', icon:Icons.home);
staticconst share =_MenuItem(text:'Share', icon:Icons.share);
staticconst settings =_MenuItem(text:'Settings', icon:Icons.settings);
staticconst logout =_MenuItem(text:'Log Out', icon:Icons.logout);
staticWidgetbuildItem(_MenuItem item) {
returnRow(
children: [
Icon(item.icon, color:Colors.white, size:22),
constSizedBox(
width:10,
),
Expanded(
child:Text(
item.text,
style:constTextStyle(
color:Colors.white,
),
),
),
],
);
}
staticvoidonChanged(BuildContext context, _MenuItem item) {
switch (item) {
case_MenuItems.home://Do somethingbreak;
case_MenuItems.settings://Do somethingbreak;
case_MenuItems.share://Do somethingbreak;
case_MenuItems.logout://Do somethingbreak;
}
}
}

Example 2 using image and openWithLongPress parameter:

Image

classPopupImageExampleextendsStatefulWidget {
constPopupImageExample({super.key});
@overrideState<PopupImageExample> createState() =>_PopupImageExampleState();
}
class_PopupImageExampleStateextendsState<PopupImageExample> {
@overrideWidgetbuild(BuildContext context) {
returnScaffold(
body:Center(
child:DropdownButtonHideUnderline(
child:DropdownButton2(
customButton:Container(
height:240,
width:240,
decoration:BoxDecoration(
borderRadius:BorderRadius.circular(40),
image:constDecorationImage(
image:AssetImage(
'assets/images/city.jpg',
),
fit:BoxFit.cover,
),
),
),
openWithLongPress:true,
items: [
..._MenuItems.firstItems.map(
(item) =>DropdownItem<_MenuItem>(
value: item,
height:48,
child:_MenuItems.buildItem(item),
),
),
constDropdownItem<Divider>(
enabled:false,
height:8,
child:Divider(),
),
..._MenuItems.secondItems.map(
(item) =>DropdownItem<_MenuItem>(
value: item,
height:48,
child:_MenuItems.buildItem(item),
),
),
],
onChanged: (value) {
_MenuItems.onChanged(context, value!as_MenuItem);
},
buttonStyleData:ButtonStyleData(
// This is necessary for the ink response to match our customButton radius.
decoration:BoxDecoration(
borderRadius:BorderRadius.circular(40),
),
),
dropdownStyleData:DropdownStyleData(
width:160,
padding:constEdgeInsets.symmetric(vertical:6),
decoration:BoxDecoration(
borderRadius:BorderRadius.circular(4),
color:Colors.redAccent,
),
offset:constOffset(40, -4),
),
menuItemStyleData:constMenuItemStyleData(
padding:EdgeInsets.only(left:16, right:16),
),
),
),
),
);
}
}
class_MenuItem {
const_MenuItem({
requiredthis.text,
requiredthis.icon,
});
finalString text;
finalIconData icon;
}
class_MenuItems {
staticconstList<_MenuItem> firstItems = [like, share, download];
staticconstList<_MenuItem> secondItems = [cancel];
staticconst like =_MenuItem(text:'Like', icon:Icons.favorite);
staticconst share =_MenuItem(text:'Share', icon:Icons.share);
staticconst download =_MenuItem(text:'Download', icon:Icons.download);
staticconst cancel =_MenuItem(text:'Cancel', icon:Icons.cancel);
staticWidgetbuildItem(_MenuItem item) {
returnRow(
children: [
Icon(
item.icon,
color:Colors.white,
size:22,
),
constSizedBox(
width:10,
),
Expanded(
child:Text(
item.text,
style:constTextStyle(
color:Colors.white,
),
),
),
],
);
}
staticvoidonChanged(BuildContext context, _MenuItem item) {
switch (item) {
case_MenuItems.like://Do somethingbreak;
case_MenuItems.share://Do somethingbreak;
case_MenuItems.download://Do somethingbreak;
case_MenuItems.cancel://Do somethingbreak;
}
}
}

7. Using DropdownButtonFormField2 with Form:

Image

finalList<String> genderItems = [
'Male',
'Female',
];
final valueListenable =ValueNotifier<String?>(null);
final _formKey =GlobalKey<FormState>();
@overrideWidgetbuild(BuildContext context) {
returnScaffold(
body:Form(
key: _formKey,
child:Padding(
padding:constEdgeInsets.symmetric(horizontal:80),
child:Column(
mainAxisAlignment:MainAxisAlignment.center,
children: [
TextFormField(
decoration:InputDecoration(
contentPadding:constEdgeInsets.all(16),
hintText:'Enter Your Full Name.',
hintStyle:constTextStyle(fontSize:14),
border:OutlineInputBorder(
borderRadius:BorderRadius.circular(15),
gapPadding:0,
),
),
),
constSizedBox(height:30),
DropdownButtonFormField2<String>(
isExpanded:true,
decoration:InputDecoration(
contentPadding:constEdgeInsets.symmetric(vertical:16, horizontal:16),
border:OutlineInputBorder(
borderRadius:BorderRadius.circular(15),
gapPadding:0,
),
// Add more decoration..
),
hint:constText(
'Select Your Gender',
style:TextStyle(fontSize:14),
),
items: genderItems
.map(
(item) =>DropdownItem<String>(
value: item,
child:Text(
item,
style:constTextStyle(
fontSize:14,
),
),
),
)
.toList(),
valueListenable: valueListenable,
validator: (value) {
if (value ==null) {
return'Please select gender.';
}
returnnull;
},
onChanged: (value) {
valueListenable.value = value;
},
iconStyleData:constIconStyleData(
icon:Icon(
Icons.arrow_drop_down,
color:Colors.black45,
),
),
dropdownStyleData:DropdownStyleData(
decoration:BoxDecoration(
borderRadius:BorderRadius.circular(15),
),
),
menuItemStyleData:constMenuItemStyleData(
useDecorationHorizontalPadding:true,
),
),
constSizedBox(height:30),
TextButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
// Do something.
}
},
child:constText('Submit Button'),
),
],
),
),
),
);
}

How to make a reusable DropdownButton2 for your app

classCustomDropdownButton2extendsStatelessWidget {
constCustomDropdownButton2({
requiredthis.hint,
requiredthis.valueListenable,
requiredthis.dropdownItems,
requiredthis.onChanged,
this.selectedItemBuilder,
this.hintAlignment,
this.valueAlignment,
this.buttonHeight,
this.buttonWidth,
this.buttonPadding,
this.buttonDecoration,
this.buttonElevation,
this.icon,
this.iconSize,
this.iconEnabledColor,
this.iconDisabledColor,
this.itemHeight,
this.itemPadding,
this.dropdownHeight,
this.dropdownWidth,
this.dropdownPadding,
this.dropdownDecoration,
this.dropdownElevation,
this.scrollbarRadius,
this.scrollbarThickness,
this.scrollbarAlwaysShow,
this.offset =Offset.zero,
super.key,
});
finalString hint;
finalValueListenable<String?>? valueListenable;
finalList<String> dropdownItems;
finalValueChanged<String?>? onChanged;
finalDropdownButton2Builder? selectedItemBuilder;
finalAlignment? hintAlignment;
finalAlignment? valueAlignment;
finaldouble? buttonHeight, buttonWidth;
finalEdgeInsetsGeometry? buttonPadding;
finalBoxDecoration? buttonDecoration;
finalint? buttonElevation;
finalWidget? icon;
finaldouble? iconSize;
finalColor? iconEnabledColor;
finalColor? iconDisabledColor;
finaldouble? itemHeight;
finalEdgeInsetsGeometry? itemPadding;
finaldouble? dropdownHeight, dropdownWidth;
finalEdgeInsetsGeometry? dropdownPadding;
finalBoxDecoration? dropdownDecoration;
finalint? dropdownElevation;
finalRadius? scrollbarRadius;
finaldouble? scrollbarThickness;
finalbool? scrollbarAlwaysShow;
finalOffset offset;
@overrideWidgetbuild(BuildContext context) {
returnDropdownButtonHideUnderline(
child:DropdownButton2<String>(
//To avoid long text overflowing.
isExpanded:true,
hint:Container(
alignment: hintAlignment,
child:Text(
hint,
overflow:TextOverflow.ellipsis,
maxLines:1,
style:TextStyle(
fontSize:14,
color:Theme.of(context).hintColor,
),
),
),
valueListenable: valueListenable,
items: dropdownItems
.map(
(String item) =>DropdownItem<String>(
value: item,
height: itemHeight ??40,
child:Container(
alignment: valueAlignment,
child:Text(
item,
overflow:TextOverflow.ellipsis,
maxLines:1,
style:constTextStyle(
fontSize:14,
),
),
),
),
)
.toList(),
onChanged: onChanged,
selectedItemBuilder: selectedItemBuilder,
buttonStyleData:ButtonStyleData(
height: buttonHeight ??40,
width: buttonWidth ??140,
padding: buttonPadding ??constEdgeInsets.only(left:14, right:14),
decoration:
buttonDecoration ??BoxDecoration(
borderRadius:BorderRadius.circular(14),
border:Border.all(
color:Colors.black45,
),
),
elevation: buttonElevation,
),
iconStyleData:IconStyleData(
icon: icon ??constIcon(Icons.arrow_forward_ios_outlined),
iconSize: iconSize ??12,
iconEnabledColor: iconEnabledColor,
iconDisabledColor: iconDisabledColor,
),
dropdownStyleData:DropdownStyleData(
//Max height for the dropdown menu & becoming scrollable if there are more items. If you pass Null it will take max height possible for the items.
maxHeight: dropdownHeight ??200,
width: dropdownWidth ??140,
padding: dropdownPadding,
decoration:
dropdownDecoration ??BoxDecoration(
borderRadius:BorderRadius.circular(14),
),
elevation: dropdownElevation ??8,
//Null or Offset(0, 0) will open just under the button. You can edit as you want.
offset: offset,
scrollbarTheme:ScrollbarThemeData(
radius: scrollbarRadius ??constRadius.circular(40),
thickness: scrollbarThickness !=null?WidgetStateProperty.all<double>(scrollbarThickness!)
:null,
thumbVisibility: scrollbarAlwaysShow !=null?WidgetStateProperty.all<bool>(scrollbarAlwaysShow!)
:null,
),
),
menuItemStyleData:MenuItemStyleData(
padding: itemPadding ??constEdgeInsets.only(left:14, right:14),
),
),
);
}
}

Thanks

If something is missing or you want to add some feature, feel free to open a ticket or contribute!

LICENSE: MIT

About

Flutter's core Dropdown Button widget with a steady dropdown menu and many other options you can customize to your needs.

Topics

Resources

Stars

357 stars

Watchers

5 watching

Forks

Releases

Packages

Used by

Contributors

Languages