I am trying to build my own drop down button with separated and condensed menu items, like the image below:
here's the code I have tried so far, I got the drop down width to match the container but the items can't be customized so far and always the height starts from above the button and is not occupying the width of the container:
body: Container(
margin: EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
child: Container(
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
border: Border.all(color: Colors.brown, width: 1.0)),
padding: EdgeInsets.fromLTRB(10.0, 5.0, 10.0, 5.0),
child: DropdownButtonHideUnderline(
child: ButtonTheme(
alignedDropdown: true,
child: DropdownButton(
isExpanded: true,
isDense: true,
value: selection,
icon: Icon(
Icons.arrow_drop_down,
color: Colors.brown,
),
iconSize: 40,
underline: Container(
height: 1,
color: Colors.transparent,
),
onChanged: (String val) => setState(() => selection = val),
items: settingsOptions.map((option) {
return DropdownMenuItem(
value: option,
child: Text(option),
);
}).toList(),
),
),
)
),
),
This is the output from the code:
How do I customize the items width, height and add dividers like in the first image? Thanks
This is an example modify as you like !
DropdownButton(
isExpanded: true,
isDense: true,
value: selection,
icon: Icon(
Icons.arrow_drop_down,
color: Colors.brown,
),
iconSize: 40,
underline: Container(
height: 1,
color: Colors.transparent,
),
onChanged: (String val) => setState(() => selection = val),
items: sampleList.map((option) {
return DropdownMenuItem(
value: option,
child: Container(
width:double.infinity,
alignment:Alignment.centerLeft,
padding: const EdgeInsets.fromLTRB(0,8.0,0,6.0),
child:Text(option),
decoration:BoxDecoration(
border:Border(top:BorderSide(color:Colors.grey,width:1))
)
),
);
}).toList(),
selectedItemBuilder:(con){
return sampleList.map((m){
return Text(m,);
}).toList();
}
)
I came across a flutter Library called dropdown_below in pub.dev. It has additional methods that can allow you customize dropdownMenuItem to your preferred layout.
DropdownBelow(
value: category,
// isDense: true,
itemWidth: MediaQuery.of(context).size.width * 0.92,
itemTextstyle: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Colors.black),
boxTextstyle: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Colors.grey.shade600),
boxPadding: EdgeInsets.fromLTRB(13, 12, 0, 12),
boxWidth: MediaQuery.of(context).size.width,
boxHeight: 60,
hint: Text('choose video'),
items: video.data.value.videos
.map((e) => DropdownMenuItem(
onTap: () => e.title,
value: e.title ?? category,
child: Container(
width: double.infinity,
decoration: BoxDecoration(),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
e.title ?? '$category',
overflow: TextOverflow.ellipsis,
),
),
),
))
.toList(),
onChanged: (video) {
context.read(videoProvider).cateroryOnChange(video);
},
),
Library Link: https://pub.dev/packages/dropdown_below
The problem with the DropdownButton is that the menu will open randomly based on the selected index and other things. Also, You can't edit its code by just trying to pass offset as the logic code of the paint work based on that and trying to hardcode the selectedItemOffset to a value won't work perfectly fine.
So use DropDownButton2 , which is built just over this .
Package: DropDownButton2
Credits: Ahmed Elsayed
For reference: Refer this link
Related
I am using DropdownButton and I am facing the following error. When opening the DropdownMenuItem list, I do not have an indent from the button itself. That is, I need to get the padding between the button (DropdownButton) and the dropdown list (DropdownMenuItem) so that there is a distance. But so far I haven't been able to do it. How can you make an indent between them?
code
#override
Widget build(BuildContext context) {
return SizedBox(
width: widget.width,
child: DropdownButtonHideUnderline(
child: DropdownButton2(
items: List.generate(
widget.items.length,
(index) => DropdownMenuItem<String>(
value: widget.items[index],
child: Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.white.withOpacity(0.1),
width: 1,
),
),
),
child: StatefulBuilder(
builder: (context, setStateSB) => GFCheckboxListTile(
value: _selectedTitles.contains(widget.items[index]),
onChanged: (bool selected) {
_onItemSelect(selected, index);
setStateSB(() {});
},
selected: selected,
title: Text(
widget.items[index],
style: constants.Styles.smallTextStyleWhite,
),
padding: const EdgeInsets.only(top: 12, bottom: 13),
margin: const EdgeInsets.only(right: 0, left: 0),
size: 22,
activeBgColor: constants.Colors.greyCheckbox,
activeBorderColor: constants.Colors.greyXMiddle,
inactiveBgColor: constants.Colors.greyCheckbox,
activeIcon: SvgPicture.asset(constants.Assets.checkboxIcon),
inactiveBorderColor: constants.Colors.greyXMiddle,
type: type,
),
),
),
),
),
hint: _selectedTitles.length > 1
? const Text('Selecte EV',
style: constants.Styles.bigBookTextStyleWhite)
: Text(_selectedTitles.join().toString(),
style: constants.Styles.bigBookTextStyleWhite),
value: selectedValue,
onChanged: (value) {
setState(() {
selectedValue = value as String;
});
},
icon: SvgPicture.asset(constants.Assets.arrowDropdown),
iconSize: 21,
buttonHeight: 27,
itemHeight: 47,
dropdownMaxHeight: 185,
dropdownWidth: 140,
dropdownDecoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: constants.Colors.purpleMain,
),
color: constants.Colors.greyDark),
selectedItemBuilder: (context) {
return widget.items.map(
(item) {
return Row(
children: [
widget.icon ?? const SizedBox(),
const SizedBox(width: 8),
Text(
item,
style: constants.Styles.bigBookTextStyleWhite,
),
],
);
},
).toList();
},
),
),
);
}
}
In the dropdown_button2 documentation, there is a property for moving the dropdown menu Offset. You can see it here https://pub.dev/documentation/dropdown_button2/latest/dropdown_button2/DropdownButton2/offset.html
On that property you just need to set an Offset, which is composed of an X and Y values.
In your case it would look something like this:
DropdownButton2(
offset: Offset(0,10),
...
),
I have a horizontal list of Categories; e.g. CARS and below it there is a dropdown menu fetch list of models per every CAR selected in the list. So my issue is after i select eg Toyota and the dropdown menu is filled with models eg - Camry, Vits, Caldina, Hilux, That is fine... but is i select one eg Camry and in the list of CARS i select another CAR eg HONDA.. the UI breaks.. can anyone tell me how i can efficiently reuse the same dropdown and just be changing the arraylist it fetchs from even when one item was selectd
I did something similar like this:
For car brand:
int selectedBrandItem;
int selectedModelItem;
int selectedByYearItem;
var listTemp;
Container(
width: _size.width,
height: _size.height* 0.08,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.0),
border: Border.all(
color: Theme.greyColor[600]
),
),
alignment: Alignment.center,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal:10.0),
child: DropdownButton(
hint: Text(allTranslations.text('brand')),
value: selectedBrandItem,
onChanged: (value){
setState(() {
selectedBrandItem = value;
selectedModelItem = null;
selectedByYearItem = null;
});
},
items: (state.response.brandList == null || state.response.brandList == [])?listTemp:state.response.brandList.map((e) =>
DropdownMenuItem(
value: e.id,
child: Text(
e.brandName,
style: TextStyle(
color: Theme.GreyColor,
fontWeight: FontWeight.w400,
fontSize: 16.0
),
)
)
).toList()??[],
isExpanded: true,
icon: Icon(Icons.keyboard_arrow_down, color: Theme.greyColor[500],),
underline: Container(color: Colors.transparent,),
),
),
);
For car model:
Container(
width: _size.width,
height: _size.height* 0.08,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.0),
border: Border.all(
color: Theme.greyColor[600]
),
),
alignment: Alignment.center,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal:10.0),
child: DropdownButton(
hint: Text(allTranslations.text('model')),
value: selectedModelItem,
onChanged: (value){
setState(() {
selectedModelItem = value;
selectedByYearItem = null;
});
},
items: state.response.modelList.map((e) =>
DropdownMenuItem(
value: e.id,
child: Text(
e.modelName,
style: TextStyle(
color: Theme.GreyColor,
fontWeight: FontWeight.w400,
fontSize: 16.0
),
)
)
).toList()??[],
isExpanded: true,
icon: Icon(Icons.keyboard_arrow_down, color: Theme.greyColor[500],),
underline: Container(color: Colors.transparent,),
),
),
);
For car year:
Container(
width: _size.width,
height: _size.height* 0.08,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.0),
border: Border.all(
color: Theme.greyColor[600]
),
),
alignment: Alignment.center,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal:10.0),
child: DropdownButton(
hint: Text(allTranslations.text('year')),
value: selectedByYearItem,
onChanged: (value){
setState(() {
selectedByYearItem = value;
});
},
items: (state.response.yearList == null || state.response.yearList == [] )?listTemp:state.response.yearList .map((e) =>
DropdownMenuItem(
value: e.id,
child: Text(
e.year,
style: TextStyle(
color: Theme.GreyColor,
fontWeight: FontWeight.w400,
fontSize: 16.0
),
)
)
).toList()??[],
isExpanded: true,
icon: Icon(Icons.keyboard_arrow_down, color: Theme.greyColor[500],),
underline: Container(color: Colors.transparent,),
),
),
);
I need to to show dropdown below the selected value . How can we set Dropdown vertical offset in flutter like we do in android. is there a simple way to it without creating custom dropdown? image is attached and
Here is my drop down code:
Container(
width: percentWidth(77, context),
padding: EdgeInsets.fromLTRB(16, 0, 16, 0),
child: DropdownButton<String>(
isExpanded: true,
value: state.selectedSubject,
underline: Container(
height: 1.0,
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.transparent,
width: 0.0))),
),
icon: Icon(Icons.arrow_drop_down),
iconSize: 0,
elevation: 16,
style: TextStyle(color: Colors.black, fontSize: 15),
onChanged: (String data) {
_userFeedbackBloc
.add(UserFeedbackEvent.onDropDownChanged(data));
},
items: spinnerItems.map<DropdownMenuItem<String>>(
(String newValue) {
return DropdownMenuItem<String>(
value: newValue,
child: Text(newValue),
);
}).toList(),
),
),
Ah, here use offset my friend.
offset: Offset(0, 100),
put that good Offset inside PopupMenuButton
You can use CustomDropDown
https://github.com/DhavalRKansara/CustomDropDown/blob/main/custom_drop_down.dart
Copy code from above line and past In dart file
Then you can use it like below
CustomDropdownButton(
value: _selectedCompany,
items: _dropdownMenuItems,
onChanged: onChangeDropdownItem,
),
I have dropdown menu with main category also subcategory.in my Worker registration i am calling this dropdown with API,so if i open worker registration page i need to select Worker as default also change the subcategory dropdown based on the default value in this case worker,in main category there is only 4 items returns
1:Worker
2:Shop
3.rental equipment
4.contractor
These are the 4 main category items.so when i open worker registration page i need worker as default in dropdown.the problem is if i initialize the value as default the subcategory drop down doesn't work
Flexible(
flex: 0,
child: Padding(
padding: EdgeInsets.only(left: 10, right: 10, top: 15),
child: Container(
width: double.infinity,
height: 45,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(5),
border: Border.all(color: Colors.blueGrey)),
child: DropdownButton(
isExpanded: true,
itemHeight: 50,
icon: Icon(Icons.arrow_drop_down),
iconSize: 40,
underline: SizedBox(),
hint: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"Category",
style: TextStyle(fontSize: 15, color: Colors.black54),
),
),
value: _selectedmenu,
onChanged: (NewValues) {
_selectedmenu = NewValues;
setState(() {});
},
items: home_model.map((menu) {
return DropdownMenuItem(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: language != "ml"
? Text(menu.english)
: Text(menu.malayalam),
),
value: menu.id,
);
}).toList(),
),
),
),
),
Set the onChanged of the DropDownButton to:
onChanged: (value) {
setState(() {
_selectedmenu = value;
});
},
And initialize _selectedmenu with the id of the Worker item, e.g.
var _selectedmenu = 'worker';
Initialized the _selectedmenu with default worker id and removed the onchanged and DropdownMenuItem and added to Text with worker with condition because i have this localization integrated into it.By passing initialized _selectedmenu to the subcategory api and called the api in setState in the main class
String _selectedmenu = "5ef1b61930def32b3cbe7890";
Flexible(
flex: 0,
child: Padding(
padding: EdgeInsets.only(left: 10, right: 10, top: 15),
child: Container(
width: double.infinity,
height: 45,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(5),
border: Border.all(color: Colors.blueGrey)),
child: DropdownButton(
isExpanded: true,
itemHeight: 50,
icon: Icon(Icons.arrow_drop_down),
iconSize: 40,
underline: SizedBox(),
hint: Padding(
padding: const EdgeInsets.all(8.0),
child: language != "ml"
? Text(
"Worker",
style:
TextStyle(fontSize: 15, color: Colors.black),
)
: Text(
"ജോലിക്കാർ",
style:
TextStyle(fontSize: 15, color: Colors.black),
),
),
value: _selectedmenu,
),
),
),
),
How can we change the width/padding of a Flutter DropdownMenuItem in a Dropdown?
Row(
children: <Widget>[
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
LightText(
text: "Agent",
),
Padding(
padding: EdgeInsets.only(top: 3),
),
Container(
height: 27,
child: Row(
children: <Widget>[
DropdownButtonHideUnderline(
child: DropdownButton<Agent>(
// isExpanded: true,
hint: Text(
agentName == null ? "" : agentName,
style: TextStyle(
fontSize: MediaQuery.of(context).size.width * 0.035,
),
),
value: selectedAgent,
onChanged: (Agent value) async {
selectedAgent = value;
agentName = selectedAgent.getAgentName();
agentId = selectedAgent.getAgentId();
},
items: agentList.map((Agent agent) {
return DropdownMenuItem<Agent>(
value: agent,
child: SizedBox(
width: 25.0,
child: LightText(
text: agent.name,
textColor: Colors.black,
),
),
);
}).toList(),
),
),
],
),
decoration: ShapeDecoration(
shape: RoundedRectangleBorder(
side: BorderSide(width: 1.0, color: lightGrey),
borderRadius: BorderRadius.all(Radius.circular(3.0)),
),
),
),
],
),
),
SizedBox(
width: 30,
),
TextBoxData(
labelText: "% Commission",
controllerText: percentageCommision,
enableVal: true,
borderColor: lightGrey,
)
],
)
Wrap Dropdown button with ButtonTheme and add alignedDropdown = true like:
ButtonTheme(
alignedDropdown: true,
child: DropdownButton(...),
)
alignedDropdown will match the menu items' width with buttons. Then we need specific width, so wrap ButtonTheme with SizedBox or Container:
SizedBox(
width: 25, // Your width for dropdowns
child: ButtonTheme(...),
)
You can control the width/padding of a Flutter DropdownMenuItems in a DropdownButton by wrapping it inside a Container widget.
Then simply assign height, width and padding to that Container widget.
Example is given below:
Widget dropDownButtonsColumn(List<String> list, String hint){
return Padding(
padding: const EdgeInsets.only(left: 40, right: 40 , bottom: 24,top:12),
child: Container(
height: 55, //gives the height of the dropdown button
width: MediaQuery.of(context).size.width, //gives the width of the dropdown button
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(3)),
color: Color(0xFFF2F2F2)
),
// padding: const EdgeInsets.symmetric(horizontal: 13), //you can include padding to control the menu items
child: Theme(
data: Theme.of(context).copyWith(
canvasColor: Colors.yellowAccent, // background color for the dropdown items
buttonTheme: ButtonTheme.of(context).copyWith(
alignedDropdown: true, //If false (the default), then the dropdown's menu will be wider than its button.
)
),
child: DropdownButtonHideUnderline( // to hide the default underline of the dropdown button
child: DropdownButton<String>(
iconEnabledColor: Color(0xFF595959), // icon color of the dropdown button
items: list.map((String value){
return DropdownMenuItem<String>(
value: value,
child: Text(value,
style: TextStyle(
fontSize: 15
),
),
);
}).toList(),
hint: Text(hint,style: TextStyle(color: Color(0xFF8B8B8B),fontSize: 15),), // setting hint
onChanged: (String value){
setState(() {
bankSelected = value; // saving the selected value
});
},
value: bankSelected, // displaying the selected value
),
)
),
),
);
}
Outputs:
With horizontal padding 50, given inside the Container widget:
Hope this helps!!