flutter

How to start PopupMenuItem below the selected item that is shown?


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?


Solution

  • Use 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),
          ],
        ),
      ),
    )