美文网首页
Flutter 长按表示弹出菜单后点击菜单跳转画面

Flutter 长按表示弹出菜单后点击菜单跳转画面

作者: 代码我写的怎么 | 来源:发表于2022-12-13 14:34 被阅读0次

    Flutter 长按表示弹出菜单后点击菜单跳转画面

    Flutter 长按表示弹出菜单

    这个比较容易,使用 PopupMenuItem 组件和系统的 showMenu 函数即可。

    示例

    1. 先定义一个 自定义菜单类

      hello_item.dart

      import 'package:flutter/material.dart';
      
      class HelloItem {
        final String text;
        final GestureTapCallback? onTap;
        const HelloItem({this.text = '', this.onTap});
      }
      
    2. 转换为 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);
        }
      }
      
    3. 主程序

      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
    
    

    相关文章

      网友评论

          本文标题:Flutter 长按表示弹出菜单后点击菜单跳转画面

          本文链接:https://www.haomeiwen.com/subject/iuphqdtx.html