Nuqo
Nuqo

Reputation: 4081

Navigator inside PopupMenuItem does not work

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

Answers (2)

WSBT
WSBT

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

Nuqo
Nuqo

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

Related Questions