This post might be dead and I know it already has an accepted answer. However, after a couple of hours of research I found the perfect solution.
You can just subclass the PopupMenuButton and override it's handleTap method like so:
class NonDismissingPopupMenuItem<T> extends PopupMenuItem<T> {
const NonDismissingPopupMenuItem(
{super.key,
super.value,
super.onTap,
super.enabled = true,
super.height = kMinInteractiveDimension,
super.padding,
super.textStyle,
super.labelTextStyle,
super.mouseCursor,
super.child});
@override
PopupMenuItemState<T, PopupMenuItem<T>> createState() => _NonDismissingPopupMenuItem<T, PopupMenuItem<T>>();
}
class _NonDismissingPopupMenuItem<T, W extends PopupMenuItem<T>> extends PopupMenuItemState<T, W> {
@override
void handleTap() {
widget.onTap?.call(); // this override prevents popup menu to close
}
}
This doesn't call the parent's handleTap and therefore doesn't close the PopupMenu while still enabling you to use a tap callback.
Credits to this SO post (which I wish I could've found earlier): How not to dismiss a PopUpMenuButton after selecting an item?