Flutter上下文菜单插件context_menus的使用

Flutter上下文菜单插件 context_menus 的使用

context_menus 是一个用于在右键单击或长按事件中显示上下文菜单的Flutter插件。本文将详细介绍如何安装、配置和使用该插件,并提供完整的示例代码。

🔨 安装

首先,在您的 pubspec.yaml 文件中添加依赖:

dependencies:
  context_menus: ^1.0.2

然后运行 flutter pub get 来安装依赖。

⚙ 导入

在需要使用 context_menus 的 Dart 文件中导入包:

import 'package:context_menus/context_menus.dart';

🕹️ 使用

基本用法

要开始使用,您需要在应用的顶层视图中包裹一个 ContextMenuOverlay

return ContextMenuOverlay(
  child: MaterialApp(
    title: 'Flutter Demo',
    theme: ThemeData(primarySwatch: Colors.blue),
    home: const MyHomePage(title: 'Flutter Demo Home Page'),
  ),
);

接下来,可以使用 ContextMenuRegion 标记哪些部分的 Widget 应触发上下文菜单:

return ContextMenuRegion(
  contextMenu: LinkContextMenu(url: 'http://flutter.dev'),
  child: TextButton(onPressed: () {}, child: Text("http://flutter.dev")),
);

每个 ContextMenuRegion 都需要一个 contextMenu 小部件,它将在右键单击或长按时显示。

内置菜单类型

  • LinkContextMenu
  • TextContextMenu
  • GenericContextMenu

这些内置菜单可以用作模板来快速构建自己的自定义菜单。

✨ 样式设置

有三种方式修改样式:

  1. 传递自定义的 ContextMenuButtonStyle 进行细微调整:

    return ContextMenuOverlay(
      buttonStyle: ContextMenuButtonStyle(
        fgColor: Colors.green,
        bgColor: Colors.red.shade100,
        hoverBgColor: Colors.red.shade200,
      ),
      child: MaterialApp(...),
    );
    
  2. 覆盖 cardBuilderbuttonBuilder 获取更多控制权:

    return ContextMenuOverlay(
      cardBuilder: (_, children) => Container(color: Colors.purple.shade100, child: Column(children: children)),
      buttonBuilder: (_, config, [__]) => TextButton(
        onPressed: config.onPressed,
        child: SizedBox(width: double.infinity, child: Text(config.label)),
      ),
      child: MaterialApp(...),
    );
    
  3. 完全自定义菜单:直接向 ContextMenuRegion 提供自定义菜单,并负责关闭菜单:

    ContextMenuRegion(
      contextMenu: MyCustomMenu(),
      child: ...,
    );
    // 在按钮按下时关闭菜单
    context.contextMenuOverlay.hide();
    

💡 自定义菜单

创建自定义菜单最简单的方式是使用 GenericContextMenu 并传递一系列 ContextMenuButtonConfig 实例:

ContextMenuRegion(
  contextMenu: GenericContextMenu(
    buttonConfigs: [
      ContextMenuButtonConfig(
        "View image in browser",
        onPressed: () => launchUrl(Uri.parse(_testImageUrl)),
      ),
      ContextMenuButtonConfig(
        "Copy image path",
        onPressed: () {
          Clipboard.setData(ClipboardData(text: _testImageUrl));
        },
      )
    ],
  ),
  child: Image.network(_testImageUrl),
);

此外,还可以通过继承 ContextMenuStateMixin 来创建更复杂的自定义菜单。

示例 Demo

以下是一个完整的示例 demo,展示了不同类型的上下文菜单及其用法:

import 'package:flutter/material.dart';
import 'package:context_menus/context_menus.dart';
import 'package:url_launcher/url_launcher.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const Scaffold(
        body: SafeArea(
          child: Column(
            children: [
              Expanded(child: DefaultMenuTests()),
              Expanded(child: StyledMenuTests()),
              Expanded(child: CustomMenuTests()),
            ],
          ),
        ),
      ),
    );
  }
}

// 默认样式菜单测试
class DefaultMenuTests extends StatelessWidget {
  const DefaultMenuTests({super.key});

  @override
  Widget build(BuildContext context) =>
      ContextMenuOverlay(child: const TestContent(title: "Default Menus"));
}

// 样式化菜单测试
class StyledMenuTests extends StatelessWidget {
  const StyledMenuTests({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.green.shade200,
      child: ContextMenuOverlay(
        cardBuilder: (context, children) {
          return Container(
            color: Colors.red,
            padding: const EdgeInsets.all(30),
            child: Column(children: children),
          );
        },
        buttonStyle: ContextMenuButtonStyle(
          fgColor: Colors.green,
          bgColor: Colors.green.shade100,
          hoverFgColor: Colors.green,
          hoverBgColor: Colors.green.shade200,
        ),
        child: const TestContent(title: "Styled Menus"),
      ),
    );
  }
}

// 自定义样式菜单测试
class CustomMenuTests extends StatelessWidget {
  const CustomMenuTests({super.key});

  @override
  Widget build(BuildContext context) {
    return ContextMenuOverlay(
      cardBuilder: (_, children) => Container(
        color: Colors.purple.shade100,
        child: Column(children: children),
      ),
      buttonBuilder: (_, config, [__]) => TextButton(
        onPressed: config.onPressed,
        child: SizedBox(width: double.infinity, child: Text(config.label)),
      ),
      child: Container(
        color: Colors.blue.shade200,
        child: const TestContent(title: "Custom Menus"),
      ),
    );
  }
}

class TestContent extends StatelessWidget {
  final String title;
  final String _testImageUrl =
      'https://images.unsplash.com/photo-1590005354167-6da97870c757?auto=format&fit=crop&w=100&q=80';

  const TestContent({
    required this.title,
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      color: Colors.transparent,
      child: Column(
        children: [
          ContextMenuRegion(
            behavior: const [
              ContextMenuShowBehavior.secondaryTap,
              ContextMenuShowBehavior.longPress,
              ContextMenuShowBehavior.tap
            ],
            contextMenu: TextContextMenu(data: title),
            child: Text(title, style: const TextStyle(fontSize: 32)),
          ),
          ContextMenuRegion(
            contextMenu: const LinkContextMenu(url: 'http://flutter.dev'),
            child: TextButton(
              onPressed: () {},
              child: const Text("http://flutter.dev"),
            ),
          ),
          ContextMenuRegion(
            contextMenu: GenericContextMenu(
              buttonConfigs: [
                ContextMenuButtonConfig(
                  "View image in browser",
                  onPressed: () => launchUrl(Uri.parse(_testImageUrl)),
                ),
                ContextMenuButtonConfig(
                  "Copy image path",
                  onPressed: () {
                    Clipboard.setData(ClipboardData(text: _testImageUrl));
                  },
                )
              ],
            ),
            child: Image.network(_testImageUrl),
          ),
        ],
      ),
    );
  }
}

这个示例展示了如何集成 context_menus 插件并实现不同的上下文菜单功能。您可以根据需求进一步定制菜单的行为和外观。


更多关于Flutter上下文菜单插件context_menus的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter上下文菜单插件context_menus的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter中使用context_menus插件来创建上下文菜单的示例代码。这个插件允许你在长按某个元素时显示一个上下文菜单。

首先,确保你已经将context_menus插件添加到你的pubspec.yaml文件中:

dependencies:
  flutter:
    sdk: flutter
  context_menus: ^x.y.z  # 请替换为最新版本号

然后,运行flutter pub get来安装依赖。

接下来,是一个完整的示例代码,展示如何在Flutter应用中使用context_menus插件:

import 'package:flutter/material.dart';
import 'package:context_menus/context_menus.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Context Menus Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final GlobalKey contextMenuKey = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Context Menus Demo'),
      ),
      body: Center(
        child: GestureDetector(
          key: contextMenuKey,
          onLongPress: () {
            showContextMenu(
              context: context,
              builder: (BuildContext context) {
                return Menu(
                  items: [
                    MenuItem(
                      value: 'Option 1',
                      onSelected: () {
                        // 处理选项1的点击
                        ScaffoldMessenger.of(context).showSnackBar(
                          SnackBar(content: Text('Option 1 selected')),
                        );
                      },
                    ),
                    MenuItem(
                      value: 'Option 2',
                      onSelected: () {
                        // 处理选项2的点击
                        ScaffoldMessenger.of(context).showSnackBar(
                          SnackBar(content: Text('Option 2 selected')),
                        );
                      },
                    ),
                    MenuItem(
                      value: 'Option 3',
                      onSelected: () {
                        // 处理选项3的点击
                        ScaffoldMessenger.of(context).showSnackBar(
                          SnackBar(content: Text('Option 3 selected')),
                        );
                      },
                    ),
                  ],
                );
              },
            );
          },
          child: Container(
            width: 200,
            height: 200,
            color: Colors.grey.withOpacity(0.5),
            child: Center(
              child: Text(
                'Long Press Me',
                style: TextStyle(fontSize: 24),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

在这个示例中,我们创建了一个简单的Flutter应用,其中包含一个GestureDetector,用于监听长按事件。当用户长按这个GestureDetector时,会显示一个上下文菜单。菜单项是通过MenuItem小部件定义的,每个菜单项都有一个value用于显示文本,以及一个onSelected回调,用于处理用户选择该菜单项时的操作。

请注意,showContextMenu函数需要一个context和一个builder参数。builder参数是一个函数,它返回一个Menu小部件,其中定义了菜单项。

希望这个示例能帮你更好地理解如何在Flutter中使用context_menus插件。如果你有任何其他问题,请随时提问!

回到顶部