Flutter中点击IconButton时如何弹出PopupMenu?
问题分析
你现在的代码无法弹出菜单的核心原因是:PopupMenuButton是一个Widget,必须被添加到Flutter的Widget树中才能渲染,而你在IconButton的onPressed回调里直接实例化它,并不会将其插入到Widget树里,所以菜单不会显示。
解决方案
方案1:直接用PopupMenuButton替代IconButton(推荐)
PopupMenuButton本身支持自定义触发图标,不需要额外套一层IconButton,代码更简洁,也符合Flutter组件的设计逻辑:
import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; String _selectedMenu = ''; enum Menu { itemOne, itemTwo, itemThree, itemFour } class ListTileWithIcon extends StatefulWidget { const ListTileWithIcon({super.key}); @override State<ListTileWithIcon> createState() => _ListTileWithIconState(); } class _ListTileWithIconState extends State<ListTileWithIcon> { @override Widget build(BuildContext context) { return ListTile( leading: const Icon(Icons.person), title: const Text("The ListTile"), // 直接用PopupMenuButton作为trailing,替代IconButton trailing: PopupMenuButton<Menu>( splashRadius: 20, // 设置触发菜单的图标 icon: const Icon( Icons.more_vert_rounded, size: 20.0, color: Color.fromARGB(255, 135, 0, 212), ), onSelected: (Menu item) { setState(() { _selectedMenu = item.name; }); }, itemBuilder: (BuildContext context) => <PopupMenuEntry<Menu>>[ const PopupMenuItem<Menu>( value: Menu.itemOne, child: Text('Item 1'), ), const PopupMenuItem<Menu>( value: Menu.itemTwo, child: Text('Item 2'), ), const PopupMenuItem<Menu>( value: Menu.itemThree, child: Text('Item 3'), ), const PopupMenuItem<Menu>( value: Menu.itemFour, child: Text('Item 4'), ), ], ), onTap: () {}, ); } }
方案2:保留IconButton,手动调用showMenu弹出菜单
如果一定要保留IconButton,可以通过showMenu方法手动触发菜单弹出,需要先计算菜单的弹出位置:
import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; String _selectedMenu = ''; enum Menu { itemOne, itemTwo, itemThree, itemFour } class ListTileWithIcon extends StatefulWidget { const ListTileWithIcon({super.key}); @override State<ListTileWithIcon> createState() => _ListTileWithIconState(); } class _ListTileWithIconState extends State<ListTileWithIcon> { @override Widget build(BuildContext context) { return ListTile( leading: const Icon(Icons.person), title: const Text("The ListTile"), trailing: IconButton( splashRadius: 20, icon: const Icon( Icons.more_vert_rounded, size: 20.0, color: Color.fromARGB(255, 135, 0, 212), ), onPressed: () async { if (kDebugMode) { print("Inside icon button's click"); } // 获取按钮在屏幕上的位置 final RenderBox button = context.findRenderObject() as RenderBox; final RenderBox overlay = Overlay.of(context).context.findRenderObject() as RenderBox; final RelativeRect position = RelativeRect.fromRect( Rect.fromPoints( button.localToGlobal(Offset.zero, ancestor: overlay), button.localToGlobal(button.size.bottomRight(Offset.zero), ancestor: overlay), ), Offset.zero & overlay.size, ); // 弹出菜单并获取选择结果 final Menu? selectedItem = await showMenu<Menu>( context: context, position: position, items: <PopupMenuEntry<Menu>>[ const PopupMenuItem<Menu>( value: Menu.itemOne, child: Text('Item 1'), ), const PopupMenuItem<Menu>( value: Menu.itemTwo, child: Text('Item 2'), ), const PopupMenuItem<Menu>( value: Menu.itemThree, child: Text('Item 3'), ), const PopupMenuItem<Menu>( value: Menu.itemFour, child: Text('Item 4'), ), ], ); if (selectedItem != null) { setState(() { _selectedMenu = selectedItem.name; }); } }, ), onTap: () {}, ); } }
内容的提问来源于stack exchange,提问作者Abdul Haadi Alhassan
相关产品推荐
相关产品推荐

