我编写了一个 Flutter 桌面 MacOS 应用程序,它使用命令行参数来处理文件:
void main(List<String> args) async {
if (args.isNotEmpty) {
runApp(MyApp(args.first))
} else ...;
}
当我从 shell 运行它时,它按预期工作:
# this command is ok:
/Applications/TommyView.app/Contents/MacOS/TommyView Pictures/hey.png
但是当我将此应用程序分配给所有 *.png 图像,并想从 Finder 运行它时,它显示:
(或者有时会出现另一个错误,具体取决于 Info.plist:
TommyView cannot open files in the “PNG image” format.
)
我还注意到执行到了
"else"
情况(即 args
为空)。
我想 Info.plist 中缺少一些魔法。请大家帮忙解答一下。
CFBundleDocumentTypes
声明应用程序处理的文件类型。
更重要的是,命令行参数并不是应用程序接收要在 macOS 上打开的文件的方式,因此 Dart 代码将无法工作。要处理文件,您需要在 macOS Runner 中实现
application(_:openFile:)
(可以随时调用,而不仅仅是在启动时调用),然后使用 平台通道将文件传递给 Dart。
谢谢@smorgan,您的回复。让我通过为其他 Flutter 开发人员添加一段代码来改进您的答案:
MainFlutterWindow.swift
添加以下内容:class MainFlutterWindow: NSWindow {
open var currentFile: String? // add this variable
override func awakeFromNib() {
...
// interop with Flutter
let channel = FlutterMethodChannel(name: "myChannel", binaryMessenger: flutterViewController.engine.binaryMessenger)
channel.setMethodCallHandler({
(call: FlutterMethodCall, result: FlutterResult) -> Void in
if (call.method == "getCurrentFile") {
result(self.currentFile)
} else {
result(FlutterMethodNotImplemented)
}
})
...
}
}
AppDelegate.swift
中你需要处理openFile
:@NSApplicationMain
class AppDelegate: FlutterAppDelegate {
...
// called when a user double-clicks on a file in Finder
//override func application(_ sender: NSApplication, openFile filename: String) -> Bool {
//(mainFlutterWindow as! MainFlutterWindow).currentFile = filename
//return true
//}
// update 2024:
override func application(_ application: NSApplication, open urls: [URL]) {
if (!urls.isEmpty) {
(mainFlutterWindow as! MainFlutterWindow).currentFile = urls.first!.path
}
}
}
main.dart
:void main(List<String> args) async {
WidgetsFlutterBinding.ensureInitialized();
final startFile = await getStartFile(args);
runApp(MyApp(startFile));
}
Future<String> getStartFile(List<String> args) async {
if (args.isNotEmpty) return args.first;
if (Platform.isMacOS) {
// in MacOS, we need to make a call to Swift native code to check if a file has been opened with our App
const hostApi = MethodChannel("myChannel");
final String? currentFile = await hostApi.invokeMethod("getCurrentFile");
if (currentFile != null) return currentFile;
}
return "";
}
Info.plist
添加文件扩展名,使其在 MacOS 推荐的应用程序中“可见”:<dict>
...
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>jpg</string>
<string>jpeg</string>
<string>png</string>
<string>gif</string>
<string>webp</string>
<string>bmp</string>
<string>wbmp</string>
</array>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
</array>
希望对你有帮助。