Skip to content

PopupMenuButton should expose useRootNavigator argument #95425

@nailgilaziev

Description

@nailgilaziev

While creating multi panel UI for wide screens sometimes you need to use several navigators. Root navigator and two nested navigators for each panel.

Currently PopupMenuButton as child of one of the nested navigators will use closest navigator to show popup menu items. This creates behavior different from what is expected. You can have multiple open popup menus when using multi panel UI!

I think people most often expect to see only one open popup on the screen at the same time. They expect that tapping outside of the presented popup menu will close it. This is how it works now at the case if you have one panel UI, but not in the case when utilizing multi panel UI with nested navigators.

Steps to Reproduce

  1. Execute flutter run on the code sample
  2. Tap on 3 dot icon button in the left AppBar
  3. Tap on 3 dot icon button in the right AppBar

Expected results:

Step2. Left popup appears
Step3. Left popup disappears

Actual results:

Step2. Left popup appears
Step3. Right popup appears, but left not disappears

My observations:

This happens because PopupMenuButton unconditionally uses closest Navigator from context.
I think that as default it should use root Navigator, but you can opt-in to use closest if needed.
internally PopupMenuButton uses showButtonMenu method that uses showMenu that has bool useRootNavigator = false argument. But currently this argument not used and it resolves to false.

Code sample
import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    debugShowCheckedModeBanner: false,
    home: TwoPanelsHome(),
  ));
}

class TwoPanelsHome extends StatelessWidget {
  const TwoPanelsHome({Key? key}) : super(key: key);

  bool hanleOnPopPage(Route<dynamic> route, dynamic result) {
    return route.didPop(result);
  }

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        Expanded(
          child: Navigator(
            onPopPage: hanleOnPopPage,
            pages: [
              TestScaffold('left/master1'),
              TestScaffold('left/master2'),
            ].map((e) => MaterialPage<void>(child: e)).toList(),
          ),
        ),
        Expanded(
          child: Navigator(
            onPopPage: hanleOnPopPage,
            pages: [
              TestScaffold('right/details1'),
              TestScaffold('right/details2')
            ].map((e) => MaterialPage<void>(child: e)).toList(),
          ),
        ),
      ],
    );
  }
}

class TestScaffold extends StatelessWidget {
  const TestScaffold(this.name, {Key? key}) : super(key: key);

  final String name;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(name),
        actions: [
          PopupMenuButton<String>(
              onSelected: (s) {
                print(s);
              },
              itemBuilder: (context) => [
                    PopupMenuItem<String>(
                      child: Text('one'),
                      value: 'one',
                    ),
                    PopupMenuItem<String>(
                      child: Text('two'),
                      value: 'two',
                    ),
                  ]),
        ],
      ),
      body: Center(child: Text('$name\npress more menu button')),
    );
  }
}
Logs
[✓] Flutter (Channel stable, 2.8.0, on macOS 12.0.1 21A559 darwin-x64, locale en-RU)
    • Flutter version 2.8.0 at /Users/ng/Tools/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision cf44000065 (8 days ago), 2021-12-08 14:06:50 -0800
    • Engine revision 40a99c5951
    • Dart version 2.15.0
Screen.Recording.2021-12-16.at.23.16.12.mov

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Issues that are less important to the Flutter projectc: new featureNothing broken; request for a new capabilityc: proposalA detailed proposal for a change to Flutterf: material designflutter/packages/flutter/material repository.frameworkflutter/packages/flutter repository. See also f: labels.team-designOwned by Design Languages teamtriaged-designTriaged by Design Languages team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions