How to limit dropdown item list and make it scrollable - flutter

I'm new to flutter and currently I'm trying to create a reusable dropdown button. The problem now is I wanted to have 15 item in my dropdown item list and it's taking all my screen space. I wanted to limit them so at first it will only show around 5-6 item and the user can scroll down the dropdown List and show the rest of the item list.
How can I achieve this ?
Here's my current code for reusable dropdown Button:
Widget build(BuildContext context) {
return Container(
decoration: widget.boxDecor,
padding: widget.padding,
margin: widget.margin,
width: widget.width,
height: widget.height,
child: Center(
child: DropdownButton<String>(
value: widget.initialValue ?? dropdownValue,
icon: widget.iconDropdown,
isExpanded: true,
elevation: 16,
style: primaryColor400Style.copyWith(
fontSize: fontSize10,
),
underline: Container(
height: 2,
decoration: BoxDecoration(
color: Colors.transparent,
),
),
onChanged: (String? newValue) {
if (newValue != 'Personal') {
Provider.of<SelectedTeamProvider>(context, listen: false)
.setSelectedTeamNameForTicket(newValue!);
} else {
Provider.of<SelectedTeamProvider>(context, listen: false)
.setSelectedTeamNameForTicket('');
}
if (widget.onChanged != null) {
widget.onChanged!(newValue!);
}
setState(() {
dropdownValue = newValue!;
});
},
items: itemLists.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value.tr()),
);
}).toList(),
),
),
);
}

You can use menuMaxHeight to control the size of the menu.
child: DropdownButton<int>(
menuMaxHeight: 300, // <-- Adjust this value to get the right number of items
...
),

Related

How to customize the dropdownlist in flutter , like flikart dropdownlist

I want to dropdownlist but i'm not able to do following things.
change the width of the dropdownbutton
make the dropdownlist to start at the dropdownbutton's height , not above it as default
adjust the height of the dropdownmenuitem
Want i want is
What i have is
The code goes like
child: ButtonTheme(
alignedDropdown: true,
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
// isDense: true,
// isExpanded: true,
itemHeight: null,
// menuMaxHeight: 10,
alignment: AlignmentDirectional.center,
elevation: 0,
value: selectedQuantity,
selectedItemBuilder: (BuildContext context) {
return _dropDownQuantities.map<Widget>((String item) {
return Container(
alignment: Alignment.center,
// color: Colors.green,
child: Text(
'Qty: $item',
style: TextStyle(fontSize: 14),
),
);
}).toList();
},
items: _dropDownQuantities.map((e) {
return DropdownMenuItem(
alignment: AlignmentDirectional.topStart,
child: Container(
child: Column(
children: [Container(child: Text(e))],
)),
value: e,
);
}).toList(),
hint: Text("Qty: 1 "),
onChanged: (value) {
setState(() {
selectedQuantity = value!;
});
}),
),
),
Use DropdownButton2 to achieve that.
Use buttonWidth property to change the width of the dropdownbutton.
Use offset property to change the position of the dropdown menu. You should add button's height to the dy offset to make it start at the dropdownbutton's height like this: Offset(0.0, "button's height")
Use itemHeight property to adjust the height of the dropdownmenuitem.
Disclaimer: I am the author of the package mentioned above.

"There should be exactly one item with [DropdownButton]'s value: Item1" error when using dropdownbutton in flutter

I am trying to use the dropdown menu in my flutter app but getting an error.
Here is the code:
List<String> items = ["Item1", "Item2", "Item3", "Item4"];
String selectedItem = "Item1";
DropdownButton<String>(
items: items.map(
(txt) {
return DropdownMenuItem<String>(
child: Text(
"$txt"
),
);
}
).toList(),
value: selectedItem,
)
In some questions, I saw that we have to initially set a variable to the value present inside our list. I have exactly done that but still getting an error.
Error Message:
There should be exactly one item with [DropdownButton]'s value: Item1.
Either zero or 2 or more [DropdownMenuItem]s were detected with the same value
'package:flutter/src/material/dropdown.dart':
Failed assertion: line 850 pos 15: 'items == null || items.isEmpty || value == null ||
items.where((DropdownMenuItem<T> item) {
return item.value == value;
}).length == 1'
What is the error here?
Kindly comment if more information is needed.
Here an example, the explanation in the code:
class _MyHomePageState extends State<MyHomePage> {
List<String> items = ["Item1", "Item2", "Item3", "Item4"];
String selectedItem = "Item1";
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Column(
children: [
Flex(direction: Axis.vertical, children:[
DropdownButton<String>(
value: selectedItem,
onChanged: (_value) { // update the selectedItem value
setState(() {
selectedItem = _value!;
});
},
items: items
.map<DropdownMenuItem<String>>((String _value) => DropdownMenuItem<String>(
value: _value, // add this property an pass the _value to it
child: Text(_value,)
)).toList(),
),
])
],
),
);
}
}
If you are loading the list from an api that returns list, look at what i did to debug the error.
Created a reusable widget that handle future response
Widget rangeLists(selectedValue) {
return FutureBuilder(
future: YourFuture,//this should return Future<List>
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Text('Loading...');
} else {
List<DropdownMenuItem<String>> categoriesItems = [
DropdownMenuItem(
child: Text(selectedValue),
value: selectedValue,
),
];
print('categoriesItems.last.value');
print(categoriesItems.last.value);
var snapshotAsMap = snapshot.data as List;
for (int i = 0; i < snapshotAsMap.length; i++) {
if (snapshotAsMap[i]['category'] != selectedValue) {
categoriesItems.add(
DropdownMenuItem(
child: Text(snapshotAsMap[i]['category']),
value: snapshotAsMap[i]['category'],
),
);
}
}
return Padding(
padding: const EdgeInsets.only(left: 18.0, right: 18, top: 10),
child: Container(
padding: EdgeInsets.only(left: 25, right: 25),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey, width: 1),
borderRadius: BorderRadius.circular(25)),
child: DropdownButton<String>(
items: categoriesItems,
icon: const Icon(
Icons.expand_more,
color: Colors.grey,
),
iconSize: 24,
elevation: 16,
isExpanded: true,
style: const TextStyle(color: Colors.grey),
underline: SizedBox(),
onChanged: (value) {
setState(() {
widget.selectedValue = value;
});
},
value: selectedValue,
hint: Text('My courses'),
),
),
);
}
})};
2.Usage
you can called it like this
String selectedValue="Select Here"
rangeLists(selectedValue)//call this as a widget in ur ui
It will handle all list from the backend u don't need to worry about the error any more
List<String> items = ["Item1", "Item2", "Item3", "Item4"];
String selectedItem = "";
DropdownButton<String>(
items: items.map(
(txt) {
return DropdownMenuItem<String>(
child: Text("$txt"),
);
}
).toList(),
value: selectedItem==""null?"":selectedItem,
)

Flutter dropdown value if not selected, then assign the initial value

I have a DropDownButtonFormField I need to check this. If the dropdown value is not selected by the user then assign the initial value when submitting.
Custom DropDown
Container myDropDownContainer(String initialVal, List<String> listItems,
String text, Function myFunc, Function validate) {
return Container(
margin: const EdgeInsets.all(8),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(
width: 120,
child: Text(
text,
style: kTextStyle,
),
),
const SizedBox(
width: 20,
),
Expanded(
child: Container(
height: 50,
decoration: BoxDecoration(
color: Colors.orangeAccent,
borderRadius: BorderRadius.circular(5)),
child: DropdownButtonFormField<String>(
autovalidateMode: AutovalidateMode.always,
//menuMaxHeight: 300,
validator: (value) {
if(value!.isEmpty) {
return "485s4a8sd4as85";
}
} ,
decoration: const InputDecoration(border: InputBorder.none),
isExpanded: true,
onTap: () => myFunc,
//borderRadius: BorderRadius.circular(5),
value: initialVal,
icon: const Icon(
Icons.arrow_downward,
color: Colors.black38,
),
iconSize: 24,
elevation: 16,
dropdownColor: Colors.deepOrange,
style: kTextStyle.copyWith(color: Colors.black),
onChanged: (val) => myFunc(val),
items: listItems.map<DropdownMenuItem<String>>((String? val) {
return DropdownMenuItem(
//TODO: Set default values
value: val,
child: Text(
val,
style: kTextStyle.copyWith(color: Colors.black),
),
);
}).toList(),
),
),
)
],
),
);
}
This is my onChanged property that assigns the selected value by the user. I added some explanations about what I am trying to do.
String _valueCinsiyet = "Diğer"; // initial value
void onChangedCinsiyet(String? newVal) {
setState(() {
if(newVal==null) {
_formData.setCinsiyet(_valueCinsiyet);
/*
'if newVal is null' means that if the value is not selected by the user
then set the initialValue( _valueCinsiyet)
*/
} else {
/*
if newVal is not null then assign the newVal( which means the selected value)
into my initialValue, then set the data to use it on different pages. What is missing?
*/
_valueCinsiyet = newVal;
_formData.setCinsiyet(_valueCinsiyet);
}
});
}
You can use nullable data to track DropdownButtonFormField changes. Being nullable you can check if it is null or not, no need to anything extra on onChanged: just assign new value usual way.
On state before build method: String? value; // value to keep track
child: DropdownButtonFormField<String>(
value: value,
onChanged: (val) {
setState(() {
value = val;
});
},
Now onSaved/submit button you can pass value by checking null, simple way is
value?? "default Value". In your case, it is value??Diğer

Flutter Prevent FutureBuilder from running twice

I am using FutureBuilder in flutter to fill up a drop down menu DropdownButtonFormField, i have the method
onChanged: (String? newValue) {
setState(() {
dropdownBrokers = newValue!;
});
},
Which allows the widget to rebuild and then assign the new value selected from the dropdown list. However, i am now experiencing a problem from my research stating that whenever the setState() is called, the future builder runs again, which makes my dropdown menu populate again. This is the full code snippet
FutureBuilder <List<Broker>>(
future: brokerData,
builder: (context, snapshot){
if(snapshot.hasData){
//do what needs to be done here
List<Broker>? data = snapshot.data;
for(var i = 0; i< data!.length; i++){
_brokers.add(data[i].firmName);
print(data[i].firmName);
}
return Container(
padding: EdgeInsets.all(10),
child: InputDecorator(
decoration: InputDecoration(
labelStyle: TextStyle(
color: Colors.grey
),
isDense: true,
contentPadding: EdgeInsets.all(7.0),
errorStyle: TextStyle(color: Colors.redAccent, fontSize: 16.0),
hintText: 'Select Broker',
border: OutlineInputBorder(borderRadius: BorderRadius.circular(5.0))),
child: DropdownButtonHideUnderline(
child: DropdownButtonFormField<String>(
alignment: Alignment.center,
value: dropdownBrokers,
icon: const Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: const TextStyle(color: Colors.indigo),
onChanged: (String? newValue) {
setState(() {
dropdownBrokers = newValue!;
});
},
items: _brokers.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
),
),
),
);
Also i get an error when i reselect an item.
How do i prevent the FutureBuilder from running twice
FutureBuilder will always be rendered once unless recreated in the build method. So the solution to this can be done in two ways:
1: Only calling brokerData in the FutureBuilder method or
2: Only calling brokerData in the initState override.
You can not do both.
And I assume the data to be populated in the dropdown comes from the future builder. Then use snapshot.data of the future instead of an already created value in this case:
items: _brokers.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
Change to:
items: snapshot.data.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);

How to change height of DropdownButton in flutter

How can I change the height of a DropdownButton in flutter.
I have tried to use Padding and SizedBox but none is realy working.
SizedBox just increases the container size while the DropdownButton is clamped to top left and therefore is not centered anymore.
Padding is ignored or moves the content outside of the button.
I do not want to change the size of the dropdown overlay but the button itself.
build(BuildContext context) {
return ThemeData(
data: ThemeData(canvasColor: Colors.white),
child: DropdownButton(
items: _items.map((item) => DropdownMenuItem(child: Text(item), value: item)).toList(),
isExpanded: true,
selectedItemBuilder: (_) {
return _items.map<Widget>((String lang) {
return Center(
widthFactor: 1,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Text(lang, style: TextStyle(color: Colors.black))
),
);
}).toList();
}
)
)
}
Wrap it in a Container, give a height, width as per your need and set isExpanded true in DropDownButton. Also change dropdownbutton text font size as per your need.
Container(
height: 50.0,
width: 200.0,
child: DropdownButton(
value: dropdownValue,
icon: Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
isExpanded: true,
style: TextStyle(color: Colors.deepPurple, fontSize: 20.0),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (String newValue) {
setState(() {
dropdownValue = newValue;
});
},
items: <String>['One', 'Two', 'Free', 'Four']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
)
)
End product should look something like this,
Looks like the 'itemHeight' property for the DropdownButton class should do the trick. I tried it and it increased the height of my DropdownButton.
Here is some sample code I have from a previous project using the itemHeight:
DropdownButton<String>(
itemHeight: 100.0,
value: selectedCurrency,
items: dropdownItems,
onChanged: (value) {
setState(() {
selectedCurrency = value;
getData();
});
},
);
Note: Just make sure the value you provide isn't less than 48.0, since it will give an error.
Docs:
itemHeight property: https://api.flutter.dev/flutter/material/DropdownButton/itemHeight.html
Minimum itemHeight defined by 'kMinInteractiveDimension':
https://api.flutter.dev/flutter/material/kMinInteractiveDimension-constant.html
itemHeight: null,
You just need to leave the itemHeight with the null value.
It will make the height of the DropdownButton with the menu item's intrinsic height.
Use SizedBox Widget
SizedBox(
height: 30,
child:DropdownButton<String>(
value: selectedCurrency,
items: dropdownItems,
onChanged: (value) {},
))
The easiest of all methods is using DropDownButton2 which is built over DropDownButton
And just change the buttonHeight attribute
Not just buttonHeight , you can even change itemHeight , buttonwidth and many more customize
String? selectedValue;
List<String> items = [
'Item1',
'Item2',
'Item3',
'Item4',
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: DropdownButtonHideUnderline(
child: DropdownButton2(,
items: items
.map((item) =>
DropdownMenuItem<String>(
value: item,
child: Text(
item,
),
))
.toList(),
value: selectedValue,
onChanged: (value) {
setState(() {
});
},
buttonHeight: 40,
buttonWidth: 140,
itemHeight: 40,
),
),
),
);
}
checkout : DropDownButton2 for more examples.
Hope it helps !!
You need to use menuMaxHeight property of DropdownButton Widget.
child: DropdownButton(
menuMaxHeight: 500.0,
value: '1',
items: setTotalPages(),
onChanged: (String? newValue) {
setState(() {
dropdownvalue = newValue!;
});
},
)
Add this property menuMaxHeight: 200, to DropdownButton