Flutter 长按表示弹出菜单后点击菜单跳转画面
Flutter 长按表示弹出菜单
这个比较容易,使用 PopupMenuItem 组件和系统的 showMenu 函数即可。
示例
-
先定义一个 自定义菜单类
hello_item.dart
import 'package:flutter/material.dart'; class HelloItem { final String text; final GestureTapCallback? onTap; const HelloItem({this.text = '', this.onTap}); }
-
转换为 opupMenuItem 组件
popmenu_util.dart
import 'package:flutter/material.dart'; import 'hello_item.dart'; class PopmenuUtil { static Future showPopupMenu(BuildContext context, LongPressStartDetails details, List<HelloItem> items) { final List<PopupMenuItem> popupMenuItems = []; for (HelloItem item in items) { PopupMenuItem popupMenuItem = PopupMenuItem( // PopupMenuItem 的坑,默认为8,点击到边矩的地方会无反应 padding: const EdgeInsets.all(0), onTap: item.onTap, child: Builder(builder: (context0) { // 这里需要使用 新的 context ,不然点击会无反应。 // 区分现有的 context return GestureDetector( behavior: HitTestBehavior.opaque, child: Container( padding: const EdgeInsets.all(8.0), child: Text(item.text), ), ); }), ); popupMenuItems.add(popupMenuItem); } RenderBox? renderBox = Overlay.of(context)?.context.findRenderObject() as RenderBox; // 表示位置(在画面边缘会自动调整位置) final RelativeRect position = RelativeRect.fromRect( Rect.fromLTRB( details.globalPosition.dx, details.globalPosition.dy, details.globalPosition.dx + 110, // 菜单显示位置X轴坐标 details.globalPosition.dy - 40, // 菜单显示位置Y轴坐标 ), Offset.zero & renderBox.size, ); return showMenu(context: context, position: position, items: popupMenuItems, useRootNavigator: true); } }
-
主程序
main.dart
import 'package:flutter/material.dart'; import 'hello_item.dart'; import 'other_page.dart'; import 'popmenu_util.dart'; void main() { runApp(const App()); } class App extends StatelessWidget { const App({super.key}); @override Widget build(BuildContext context) { return const MaterialApp( home: HomePage(), ); } } class HomePage extends StatefulWidget { const HomePage({super.key}); @override State<HomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<HomePage> { @override Widget build(BuildContext context) { final List<HelloItem> items = []; items.add( HelloItem( text: 'Hello', onTap: () { debugPrint('Hello'); // Navigator.of(context).pop(); }, ), ); items.add( HelloItem( text: 'World', onTap: () { debugPrint('World'); Navigator.of(context).push(MaterialPageRoute(builder: (context) { return const OtherPage(); })); }, ), ); return Scaffold( appBar: AppBar( title: const Text('长按右键菜单'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ GestureDetector( behavior: HitTestBehavior.opaque, onLongPressStart: (details) async { if (items.isNotEmpty) { await PopmenuUtil.showPopupMenu(context, details, items); } }, onTap: () { // 单击处理 }, child: Container( color: Colors.cyan, padding: const EdgeInsets.all(8.0), child: const Text('Hello, world'), ), ), ], ), ), ); } }
问题
这里虽然能正常表示右键菜单,但是点击菜单无法进行正常的画面跳转。
Flutter 长按表示弹出菜单后点击菜单画面迁移
要使画面正常跳转,首先不能使用 PopupMenuItem 的 onTap 事件,其次是 PopupMenuItem 的子组件使用 Builder 构建,并使用 GestureDetector 响应事件。
使用 Builder 构建子组件里参数使用不同的 BuildContext 。
成功跳转:
修改内容
上面的 popmenu_util.dart 里的 showPopMenu 改为如下内容:
```dart
static Future showPopMenu(BuildContext context, LongPressStartDetails details, List<HelloItem> items) {
final List<PopupMenuItem> popupMenuItems = [];
for (HelloItem item in items) {
PopupMenuItem popupMenuItem = PopupMenuItem(
// PopupMenuItem 的坑,默认为8,点击到边矩的地方会无反应
padding: const EdgeInsets.all(0),
// 不使用 PopupMenuItem 的 onTap 事件
// onTap: item.onTap,
child: Builder(builder: (context0) {
// 这里需要使用 新的 context ,不然点击会无反应。
// 区分现有的 context
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
if (item.onTap != null) {
item.onTap!();
}
},
child: Row(children: [
Expanded(
child: Text(item.text),
),
]),
);
}),
);
popupMenuItems.add(popupMenuItem);
}
RenderBox? renderBox = Overlay.of(context)?.context.findRenderObject() as RenderBox;
// 表示位置(在画面边缘会自动调整位置)
final RelativeRect position = RelativeRect.fromRect(
Rect.fromLTRB(
details.globalPosition.dx,
details.globalPosition.dy,
details.globalPosition.dx + 110, // 菜单显示位置X轴坐标
details.globalPosition.dy - 40, // 菜单显示位置Y轴坐标
),
Offset.zero & renderBox.size,
);
return showMenu(context: context, position: position, items: popupMenuItems, useRootNavigator: true);
}
## 一个小坑
> PopupMenuItem 的默认内边矩是 8 ,点击这个内边矩的位置,不会触发实际的处理。
>作者:bettersun
链接:https://juejin.cn/post/7176140672049774650
网友评论