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 ,点击这个内边矩的方位,不会触发实践的处理。