PopupMenuButton Patterns in Flutter: Overflow Menus Done Right
PopupMenuButton vs PopupMenu, itemBuilder contracts, onSelected and onCanceled, initialValue checks, and the menu-button pattern desktop apps expect.
Published on • November 4, 2026
AI Assistant

Every app has that three-dot menu: sort, refresh, settings. Flutter’s PopupMenuButton builds it for you — an anchor widget that opens a PopupMenu positioned against itself (docs.flutter.dev).
The Standard Overflow Menu
AppBar(
actions: [
PopupMenuButton<String>(
onSelected: (value) {
switch (value) {
case 'refresh': _refresh();
case 'settings': context.go('/settings');
}
},
itemBuilder: (context) => const [
PopupMenuItem(value: 'refresh', child: Text('Refresh')),
PopupMenuItem(value: 'settings', child: Text('Settings')),
],
),
],
)
itemBuilder runs every time the menu opens — build items from current state, not from a cached list. Each PopupMenuItem needs a value; that value is what onSelected receives.
Checked Items With initialValue
For menus that reflect current selection (view mode, sort order), pass initialValue and mark the active entry:
PopupMenuButton<String>(
initialValue: _sortOrder,
onSelected: (v) => setState(() => _sortOrder = v),
itemBuilder: (context) => const [
PopupMenuItem(
value: 'name',
child: ListTile(
leading: Icon(Icons.sort_by_alpha),
title: Text('Name'),
contentPadding: EdgeInsets.zero,
),
),
PopupMenuItem(
value: 'date',
child: ListTile(
leading: Icon(Icons.schedule),
title: Text('Date'),
contentPadding: EdgeInsets.zero,
),
),
],
)
The selected item gets a checkmark via CheckedPopupMenuItem semantics; using a ListTile inside gives you icon + label rows that match Material’s menu spec (48dp minimum height comes free).
Icon-Only Anchors
The button itself is customizable through icon, child, or by wrapping your own widget with MenuItemButton-style anchors:
PopupMenuButton<String>(
tooltip: 'More actions', // accessibility, not optional
icon: const Icon(Icons.more_vert),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
elevation: 2,
onSelected: _handle,
itemBuilder: _buildItems,
)
Always set tooltip on icon-only anchors — it becomes the semantic label for screen readers and the hover hint on desktop.
onCanceled and the Dismiss Path
Menus close three ways: a selection (onSelected), an outside tap/dismiss gesture, or Navigator.pop without a value. onCanceled fires for the second case — use it to roll back optimistic state or close an accompanying overlay, not to log analytics:
PopupMenuButton(
onSelected: _commitTempSort,
onCanceled: _revertTempSort,
itemBuilder: _buildItems,
)
PopupMenuButton vs. PopupMenu vs. MenuAnchor
PopupMenuButton— self-contained: anchor + menu + callbacks. Use for app bars and card overflow.PopupMenu— the raw menu surface, for custom positioning or show-on-build flows.MenuAnchor— the modern replacement for complex cases: it supports submenus, keyboard navigation, and customMenuControlleropening, and is the direction Material is heading.
If you’re starting fresh on desktop-first apps with nested menus, reach for MenuAnchor; for the classic mobile overflow, PopupMenuButton remains the shortest correct path.
Build items from live state, label your icon-only anchors, and handle onCanceled — and overflow menus behave the way users expect on every platform.