I have this code:
PopupMenuButton<Guru>(
onSelected: _onSelectGuru,
itemBuilder: (context) => [
...widget.wisdomData.gurus.map(
(guru) => PopupMenuItem<Guru>(
value: guru,
child: Text(
guru.name,
style: const TextStyle(color: Colors.white),
),
),
),
const PopupMenuItem<Guru>(
value: null,
child: Text(
'Random',
style: TextStyle(color: Colors.white),
),
),
],
position: PopupMenuPosition.under,
color: Colors.transparent,
elevation: 0,
child: Container(
decoration: const BoxDecoration(color: Colors.transparent),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
selectedGuru.name,
style: const TextStyle(color: Colors.white),
),
const Icon(
Icons.arrow_drop_down,
color: Colors.white,
),
],
),
),
)
The issue is that if the number of PopupMenuItem
's is small (e.g., 2 to 7–9), it starts from the correct position. However, when the number of items is large (more than 10), it starts from the top of the screen instead of below the selected item. How can I ensure that it always starts below the selected item and scrolls to display all the items?
I have this code:
PopupMenuButton<Guru>(
onSelected: _onSelectGuru,
itemBuilder: (context) => [
...widget.wisdomData.gurus.map(
(guru) => PopupMenuItem<Guru>(
value: guru,
child: Text(
guru.name,
style: const TextStyle(color: Colors.white),
),
),
),
const PopupMenuItem<Guru>(
value: null,
child: Text(
'Random',
style: TextStyle(color: Colors.white),
),
),
],
position: PopupMenuPosition.under,
color: Colors.transparent,
elevation: 0,
child: Container(
decoration: const BoxDecoration(color: Colors.transparent),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
selectedGuru.name,
style: const TextStyle(color: Colors.white),
),
const Icon(
Icons.arrow_drop_down,
color: Colors.white,
),
],
),
),
)
The issue is that if the number of PopupMenuItem
's is small (e.g., 2 to 7–9), it starts from the correct position. However, when the number of items is large (more than 10), it starts from the top of the screen instead of below the selected item. How can I ensure that it always starts below the selected item and scrolls to display all the items?
2 Answers
Reset to default 1Use the constraints property inside SingleChildScrollView
to limit the height of the menu so it does not expand beyond a certain size.
PopupMenuButton<Guru>(
onSelected: _onSelectGuru,
itemBuilder: (context) => [
PopupMenuItem(
enabled: false,
child: ConstrainedBox(
constraints: BoxConstraints(
maxHeight: 300, // Adjust height as needed
),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
...widget.wisdomData.gurus.map(
(guru) => PopupMenuItem<Guru>(
value: guru,
child: Text(guru.name, style: const TextStyle(color: Colors.white)),
),
),
const PopupMenuItem<Guru>(
value: null,
child: Text('Random', style: TextStyle(color: Colors.white)),
),
],
),
),
),
),
],
position: PopupMenuPosition.under,
color: Colors.black,
elevation: 4,
child: Container(
decoration: const BoxDecoration(
color: Colors.transparent,
),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(selectedGuru.name, style: const TextStyle(color: Colors.white)),
const Icon(Icons.arrow_drop_down, color: Colors.white),
],
),
),
)
To make the PopupMenuButton in your code always start below the selected item and scroll when there are many items, add the constraints property to limit its height
PopupMenuButton<Guru>(
onSelected: _onSelectGuru,
itemBuilder: (context) => [
...widget.wisdomData.gurus.map(
(guru) => PopupMenuItem<Guru>(
value: guru,
child: Text(guru.name, style: const TextStyle(color: Colors.white)),
),
),
const PopupMenuItem<Guru>(
value: null,
child: Text('Random', style: TextStyle(color: Colors.white)),
),
],
position: PopupMenuPosition.under,
constraints: BoxConstraints(maxHeight: 300), // Adjust height as needed
color: Colors.transparent,
elevation: 0,
child: Container(
decoration: const BoxDecoration(color: Colors.transparent),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(selectedGuru.name, style: const TextStyle(color: Colors.white)),
const Icon(Icons.arrow_drop_down, color: Colors.white),
],
),
),
),
This ensures the menu stays below the selected item and scrolls if the item count exceeds the specified maxHeight.