Flutter Desktop macOS应用:解决Finder打开文件参数为空及格式报错问题
解决Flutter Desktop macOS应用从Finder打开文件无参数问题
1. 完善Info.plist的文件关联配置
在macos/Runner/Info.plist中添加以下配置,声明应用支持PNG文件类型:
<key>CFBundleDocumentTypes</key> <array> <dict> <key>CFBundleTypeName</key> <string>PNG Image</string> <key>CFBundleTypeRole</key> <string>Viewer</string> <key>LSHandlerRank</key> <string>Default</string> <key>LSItemContentTypes</key> <array> <string>public.png</string> </array> </dict> </array> <!-- 如需支持更多图片格式,可在LSItemContentTypes数组中添加对应UTI,比如public.jpeg、public.gif等 -->
这段配置会告知macOS你的应用具备PNG文件的处理能力,CFBundleTypeRole设为Viewer明确应用作为查看器角色,LSHandlerRank设为Default可让应用成为该类型文件的默认打开选项之一。
2. 处理macOS原生文件打开事件(关键)
Flutter Desktop在macOS环境下,通过Finder打开文件时,文件路径不会通过main函数的args传递,必须通过macOS的ApplicationDelegate接收。修改macos/Runner/AppDelegate.swift:
import Cocoa import FlutterMacOS @NSApplicationMain class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } // 新增方法接收打开的文件路径 override func application(_ sender: NSApplication, openFiles filenames: [String]) { // 获取Flutter视图控制器,创建MethodChannel传递数据到Dart层 guard let controller = mainFlutterWindow?.contentViewController as? FlutterViewController else { return } let channel = FlutterMethodChannel(name: "com.tommyview.file_opener", binaryMessenger: controller.engine.binaryMessenger) // 传递第一个文件路径(需处理多文件可遍历filenames数组) if let firstFile = filenames.first { channel.invokeMethod("openFile", arguments: firstFile) } } }
3. 在Dart代码中监听文件路径
修改Dart代码,同时兼容Shell启动的命令行参数和Finder启动的事件传递:
import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'dart:io'; void main() async { WidgetsFlutterBinding.ensureInitialized(); const channel = MethodChannel('com.tommyview.file_opener'); String? initialFile; // 兼容Shell启动的命令行参数 final executableArgs = Platform.executableArguments; if (executableArgs.isNotEmpty) { initialFile = executableArgs.first; } // 监听来自macOS的文件打开事件 channel.setMethodCallHandler((call) async { if (call.method == 'openFile') { final filePath = call.arguments as String; runApp(MyApp(filePath)); } return null; }); // 根据有无文件启动对应界面 if (initialFile != null) { runApp(MyApp(initialFile)); } else { runApp(const MyApp(null)); } } class MyApp extends StatelessWidget { final String? filePath; const MyApp(this.filePath, {super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(title: const Text('TommyView')), body: Center( child: filePath != null ? Text('Opening file: $filePath') : const Text('No file selected'), ), ), ); } }
4. 重新打包应用
修改完成后,重新构建并替换应用:
flutter build macos
将构建好的TommyView.app复制到/Applications目录,重新关联PNG文件后,即可正常从Finder打开文件。
注意事项
MethodChannel的名称需保证Swift和Dart代码完全一致,可根据自己的应用包名调整。- 如需支持多文件批量打开,可在Swift代码中遍历
filenames数组,逐个传递给Dart层处理。 - 测试前请确保旧版本应用已完全替换,避免缓存导致配置不生效。
内容的提问来源于stack exchange,提问作者Mitrakov Artem
相关产品推荐
相关产品推荐

