You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 03:50:48