Flutter - 在 PopupMenuButton onTap 中导航不起作用

fin*_*tis 6 dart flutter flutter-navigation

我曾经使用 IconButton 的onpressed从我的 AppBar 导航到设置页面,这是有效的。现在我试图从 PopupMenuItem 的onTap触发导航,但页面不导航。两个小部件都位于同一层次结构中,我无法找出不同行为的原因。不会抛出任何错误。

这是我的 appBar 的代码,其中包含操作:

  Widget build(BuildContext ctx) {
    return Container(
      child: Scaffold(
        appBar: AppBar(
          title: const Text('MyApp'),
          actions: [
            PopupMenuButton(
              itemBuilder: (context) => [
// THE NAVIGATION IN onTap DOES NOT WORK
                PopupMenuItem(
                  child: Text(AppLocalizations.of(context).settings),
                  onTap: () => _openSettings(ctx),
                ),
              ],
              icon: Icon(
                Icons.settings,
              ),
            ),
// THIS WORKS
            IconButton(onPressed: () => _openSettings(ctx), 
                            icon: Icon(Icons.settings))
          ],
        ),
        body: Text("")  
      ),
    );
  }
Run Code Online (Sandbox Code Playgroud)

这里的导航调用只能在 IconButton 的onpressed内部工作。我可以确认该函数在这两种情况下都被触发:

  Future<void> _openSettings(BuildContext ctx) async {
    print('settings');
    await  Navigator.push(
        ctx, MaterialPageRoute(builder: (ctx) => SettingsPage()));
    print('settings completed');
  }
Run Code Online (Sandbox Code Playgroud)

我将不胜感激任何帮助!

解决方法: 我还没有发现onTap导航的问题是什么,但现在我只是使用onSelected,它会产生相同的用户体验并且有效:

Widget build(BuildContext ctx) {
    return Container(
      child: Scaffold(
        appBar: AppBar(
          title: const Text('MyApp'),
          actions: [
            PopupMenuButton(
              onSelected: (result){
                switch(result){
                  case 0: _openSettings(); break;
                  case 1: _signOut(); break;
                }
              },
              itemBuilder: (context) => [
                PopupMenuItem(
                  child: Text(AppLocalizations.of(context).settings),
                value: 0
                ),
                PopupMenuItem(
                  child: Text(AppLocalizations.of(context).logout),
                  value: 1
                )
              ],
              icon: Icon(
                Icons.settings,
              ),
            )
          ],
        )
      )
    );
 }
Run Code Online (Sandbox Code Playgroud)

Mos*_*riy 6

我也遇到了这个问题并解决如下:

  1. 首先你必须删除onTap或赋予它的值null
  2. value其次,为中的参数赋值PopupMenuItem。您可以提供任何类型,例如intString或任何您想要的类型,因为值是动态的。
  3. 然后,将函数传递onSelectedPopupMenuButton.
  4. 在该onSelected功能内,您可以选择按下任何菜单项时要执行的操作。

在你的情况下,你的代码应该是这样的

  PopupMenuButton(
    itemBuilder: (context) => [
      PopupMenuItem(
        child: Text(...),
        value: 'settings',
      ),
    ],
    onSelected: (value){
      if(value == 'settings'){
        _openSettings(ctx);
      }
    },
  ),
Run Code Online (Sandbox Code Playgroud)


Hoo*_*yar 1

您需要使用 调用 future 函数()=>,并传递Build Context该函数以了解要从哪个上下文导航。

所以你的按钮会像这样改变:

   IconButton(onPressed:()=> _openSettings(context), icon: Icon(Icons.settings))
Run Code Online (Sandbox Code Playgroud)

你的函数应该是这样的:

Future<void> _openSettings(BuildContext ctx) async {
    print('settings');
    await Navigator.push(
        ctx, MaterialPageRoute(builder: (context) => SettingPage()));
    print('settings completed');
  }
Run Code Online (Sandbox Code Playgroud)
这是我测试过该问题的完整代码:
class PlayGround extends StatefulWidget {
  const PlayGround({Key? key}) : super(key: key);

  @override
  _PlayGroundState createState() => _PlayGroundState();
}

class _PlayGroundState extends State<PlayGround> {
  final _title =
      TextEditingController.fromValue(TextEditingValue(text: 'initialTitle'));

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('MyApp'),
          actions: [
            PopupMenuButton<int>(
              itemBuilder: (context) => [
                PopupMenuItem(
                  child: Text(AppLocalizations.of(context).settings),
                  onTap: () {},
                ),
                PopupMenuItem(
                  child: Text(AppLocalizations.of(context).logout),
                  onTap: () {},
                )
              ],
              icon: Icon(
                Icons.settings,
              ),
            ),
            IconButton(
                onPressed: () => _openSettings(context),
                icon: Icon(Icons.settings))
          ],
        ),
        body: Container());
  }

  Future<void> _openSettings(BuildContext ctx) async {
    print('settings');
    await Navigator.push(
        ctx, MaterialPageRoute(builder: (context) => SettingPage()));
    print('settings completed');
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)