Flutter原生上下文菜单插件native_context_menu_ng的使用
Flutter原生上下文菜单插件native_context_menu_ng的使用
native_context_menu_ng
是一个为Flutter应用程序提供原生右键菜单的插件,支持三大桌面平台(macOS、Windows和Linux)。
特性
- 支持子菜单;
- 支持图标(目前仅支持PNG格式以保持平台一致性);
- 图标大小无需手动指定,它会根据平台硬件和缩放设置自动调整到最佳尺寸;
- 支持分隔符;
- 支持启用/禁用菜单项。
使用方法
步骤1:构建菜单
首先,我们需要构建一个菜单。这可以通过调用 initMenu()
函数来完成。该函数返回一个 NativeMenu
对象。
Future<NativeMenu> initMenu() async {
// 如果有子菜单,则此项目无操作。
NativeMenuItem itemNew = NativeMenuItem.simple(title: "New");
// 从资源文件加载图标
Uint8List iconText = await VenyoreImageUtil.assetImageToUint8List("assets/images/txt.png");
Uint8List iconWord = await VenyoreImageUtil.assetImageToUint8List("assets/images/word.png");
Uint8List iconExcel = await VenyoreImageUtil.assetImageToUint8List("assets/images/excel.png");
Uint8List iconPdf = await VenyoreImageUtil.assetImageToUint8List("assets/images/pdf.png");
NativeMenu subMenu = NativeMenu();
subMenu.addItem(NativeMenuItem.withRawIcon(title: "Text", action: "action_text", rawIcon: iconText));
subMenu.addItem(NativeMenuItem.withRawIcon(title: "Word", action: "action_word", rawIcon: iconWord));
subMenu.addItem(NativeMenuItem.withRawIcon(title: "Excel", action: "action_excel", rawIcon: iconExcel));
subMenu.addItem(NativeMenuItem.withRawIcon(title: "PDF", action: "action_pdf", rawIcon: iconPdf));
itemNew.subMenu = subMenu;
// 从本地路径加载图标
// 注意:本地路径图标在应用沙盒内有限制
String iconPath;
if (Platform.isMacOS) {
iconPath = "/Users/parcool/Downloads/paste.png";
} else if (Platform.isLinux) {
iconPath = "/home/parcool/Downloads/paste.png";
} else if (Platform.isWindows) {
iconPath = "C:\\Users\\parcool\\Downloads\\paste.png";
} else {
throw PlatformException(code: "Unsupported platform!");
}
NativeMenu menu = NativeMenu();
menu.addItem(itemNew);
menu.addItem(NativeMenuItem.simple(title: "New Folder", action: "action_new_folder"));
menu.addItem(NativeMenuItem.separator());
menu.addItem(NativeMenuItem.withIcon(title: "Paste", action: "action_paste", icon: iconPath, isEnable: false));
return menu;
}
步骤2:添加右键菜单
方法1:使用 NativeContextMenuWidget
将您的小部件作为子部件放入 NativeContextMenuWidget
中。
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Venyore 插件示例应用'),
),
body: FutureBuilder<NativeMenu>(
future: initMenu(),
builder: (BuildContext context, AsyncSnapshot<NativeMenu> snapshot) {
if (snapshot.hasData) {
return NativeContextMenuWidget(
actionCallback: (action) {
final actionString = action.toString();
// switch (actionString) {
// case "action...":
// break;
// }
},
menu: snapshot.requireData,
otherCallback: (method) {
if (kDebugMode) {
print("$method 被调用了!");
}
},
child: const Text("您的自定义小部件"),
);
} else if (snapshot.hasError) {
return const Text("构建 NativeMenu 时出错。");
} else {
return const CircularProgressIndicator();
}
},
),
),
);
}
方法2:直接使用 NativeContextMenuNg
处理鼠标事件并根据需要弹出菜单。
void showNativeContextMenu() async {
final nativeContextMenuNgPlugin = NativeContextMenuNg()
..setMethodCallHandler((call) async {
switch (call.method) {
case "onItemClicked":
final arg = call.arguments as Map<dynamic, dynamic>;
final actionString = arg["action"].toString();
// switch (actionString) {
// case "action...":
// break;
// }
break;
case "menuDidClose":
if (kDebugMode) {
print("menuDidClose 被调用了!");
}
break;
default:
break;
}
});
await nativeContextMenuNgPlugin.showNativeContextMenu(widget.menu);
}
示例代码
以下是完整的示例代码:
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:native_context_menu_ng/native_context_menu_widget.dart';
import 'package:native_context_menu_ng/native_menu.dart';
import 'package:native_context_menu_ng/venyore_image_util.dart';
import 'package:image/image.dart' as img;
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
[@override](/user/override)
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Future<Uint8List> convertBMPtoUint8List(String assetPath, {int? width, int? height}) async {
final ByteData byteData = await rootBundle.load(assetPath);
final Uint8List bmpBytes = byteData.buffer.asUint8List();
if (width != null && height != null) {
final img.Image? bmpImage = img.decodeImage(bmpBytes);
if (bmpImage != null) {
final img.Image bmpImageResized = img.copyResize(bmpImage, width: width, height: height);
return Future.value(Uint8List.fromList(img.encodePng(bmpImageResized)));
}
}
return Future.value(bmpBytes);
}
String? _clickedAction;
Future<NativeMenu> initMenu() async {
// 如果有子菜单,则此项目无操作。
NativeMenuItem itemNew = NativeMenuItem.simple(title: "New");
// 从资源文件加载图标
Uint8List iconText = await VenyoreImageUtil.assetImageToUint8List("assets/images/txt.png");
Uint8List iconWord = await VenyoreImageUtil.assetImageToUint8List("assets/images/word.png");
Uint8List iconExcel = await VenyoreImageUtil.assetImageToUint8List("assets/images/excel.png");
Uint8List iconPdf = await VenyoreImageUtil.assetImageToUint8List("assets/images/pdf.png");
NativeMenu subMenu = NativeMenu();
subMenu.addItem(NativeMenuItem.withRawIcon(title: "Text", action: "action_text", rawIcon: iconText));
subMenu.addItem(NativeMenuItem.withRawIcon(title: "Word", action: "action_word", rawIcon: iconWord));
subMenu.addItem(NativeMenuItem.withRawIcon(title: "Excel", action: "action_excel", rawIcon: iconExcel));
subMenu.addItem(NativeMenuItem.withRawIcon(title: "PDF", action: "action_pdf", rawIcon: iconPdf));
itemNew.subMenu = subMenu;
// 从本地路径加载图标
// 注意:本地路径图标在应用沙盒内有限制
String iconPath;
if (Platform.isMacOS) {
iconPath = "/Users/parcool/Downloads/paste.png";
} else if (Platform.isLinux) {
iconPath = "/home/parcool/Downloads/paste.png";
} else if (Platform.isWindows) {
iconPath = "C:\\Users\\parcool\\Downloads\\paste.png";
} else {
throw PlatformException(code: "Unsupported platform!");
}
NativeMenu menu = NativeMenu();
menu.addItem(itemNew);
menu.addItem(NativeMenuItem.simple(title: "New Folder", action: "action_new_folder"));
menu.addItem(NativeMenuItem.separator());
menu.addItem(NativeMenuItem.withIcon(title: "Paste", action: "action_paste", icon: iconPath, isEnable: false));
return menu;
}
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Venyore 插件示例应用'),
),
body: FutureBuilder<NativeMenu>(
future: initMenu(),
builder: (BuildContext context, AsyncSnapshot<NativeMenu> snapshot) {
if (snapshot.hasData) {
return NativeContextMenuWidget(
actionCallback: (action) {
final actionString = action.toString();
setState(() {
_clickedAction = actionString;
});
// switch (actionString) {
// case "action1":
// break;
// case "action2":
// break;
// default:
// break;
// }
},
menu: snapshot.requireData,
otherCallback: (method) {
if (kDebugMode) {
print("$method 被调用了!");
}
},
child: Container(
color: Colors.grey[350],
child: Column(
children: [
SizedBox(
height: 100,
child: Align(
alignment: Alignment.bottomCenter,
child: Text(
"点击的菜单动作: $_clickedAction",
style: TextStyle(color: Colors.green[600]),
),
)),
Expanded(
child: Center(
child: Text('右键点击此处。', style: TextStyle(color: Colors.grey[500])),
),
),
],
),
),
);
} else if (snapshot.hasError) {
return const Text("构建 NativeMenu 时出错。");
} else {
return const CircularProgressIndicator();
}
},
),
),
);
}
}
更多关于Flutter原生上下文菜单插件native_context_menu_ng的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter原生上下文菜单插件native_context_menu_ng的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
native_context_menu_ng
是一个 Flutter 插件,用于在 Flutter 应用中显示原生平台的上下文菜单。它允许开发者在不离开 Flutter 环境的情况下,使用平台原生的上下文菜单功能。这对于需要在特定上下文(例如长按某些元素)中显示菜单的场景非常有用。
安装插件
首先,你需要在 pubspec.yaml
文件中添加 native_context_menu_ng
依赖:
dependencies:
flutter:
sdk: flutter
native_context_menu_ng: ^1.0.0 # 请使用最新版本
然后,运行 flutter pub get
来安装插件。
使用插件
-
导入插件
在你的 Dart 文件中导入
native_context_menu_ng
插件:import 'package:native_context_menu_ng/native_context_menu_ng.dart';
-
创建上下文菜单
你可以使用
NativeContextMenu
类来创建上下文菜单。以下是一个简单的示例,展示如何在长按某个部件时显示上下文菜单:import 'package:flutter/material.dart'; import 'package:native_context_menu_ng/native_context_menu_ng.dart'; class ContextMenuExample extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Native Context Menu Example'), ), body: Center( child: GestureDetector( onLongPress: () async { final menu = NativeContextMenu( items: [ NativeContextMenuItem( title: 'Copy', onSelected: () { print('Copy selected'); }, ), NativeContextMenuItem( title: 'Paste', onSelected: () { print('Paste selected'); }, ), NativeContextMenuItem( title: 'Delete', onSelected: () { print('Delete selected'); }, ), ], ); await menu.show(context); }, child: Container( padding: EdgeInsets.all(20), color: Colors.blue, child: Text( 'Long press here', style: TextStyle(color: Colors.white), ), ), ), ), ); } } void main() { runApp(MaterialApp( home: ContextMenuExample(), )); }