Reputation: 4081
When trying to call Navigator
inside the onTap
property of a PopupMenuItem
it doesn't work:
PopupMenuButton(itemBuilder: (BuildContext context) {
return [
PopupMenuItem(
child: Text('Edit'),
onTap: () => Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => EditorPage())),
),
];
}),
Upvotes: 18
Views: 5291
Reputation: 36323
When a pop up menu is clicked, it will call pop()
on the navigator to dismiss itself. So pushing an extra route would cause it to pop that route immediately, instead of dismissing itself.
Knowing this, we can change the order of the two events by slightly delaying the push()
operation by 0 ms
. This causes the callback to be pushed on to the event queue and thus will be executed after the menu is popped.
PopupMenuItem(
child: Text('Open Settings'),
onTap: () async {
final navigator = Navigator.of(context);
await Future.delayed(Duration.zero);
navigator.push(
MaterialPageRoute(builder: (_) => SettingsPage()),
);
},
)
We can further shorten the above code to:
onTap: () => Future(
() => Navigator.of(context).push(
MaterialPageRoute(builder: (_) => SettingsPage()),
),
)
Upvotes: 25
Reputation: 4081
The popup menu is popping its own route before calling the callback, a way to fix this is to use the onSelected
property of the PopupMenuButton
itself:
//---add onSelected to your PopupMenuButton
PopupMenuButton(onSelected: (result) {
if (result == 0) {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => EditorPage()));
}
}, itemBuilder: (BuildContext context) {
return [
PopupMenuItem(
value: 0, //---add this line
child: Text('Edit'),
),
];
}),
Upvotes: 33