Flutter macOS + Vision 手势识别踩坑实录
Flutter macOS ≠ 原生 macOS App不要接管 AppDelegate 生命周期原生初始化放在 MainFlutterWindowselector 问题请直接检查最终二进制这个问题不是代码能力问题,而是架构认知问题。一旦理解 Flutter macOS 的真实运行机制,后续所有原生扩展都会非常顺畅。
—— unrecognized selector applicationDidFinishLaunching 的根因分析与终极解法
技术栈:Flutter 3.x / macOS 13 / Xcode 15 / Swift / Vision
一、背景说明
最近在做一个 Flutter macOS 项目,目标是:
-
使用 macOS 摄像头
-
基于 Vision(VNDetectHumanHandPoseRequest)进行手势识别
-
通过 手指移动 控制 Flutter 页面中的 小球左右移动与缩放
整体技术结构如下:
-
Flutter
-
页面 UI
-
动画渲染
-
玻璃球效果
-
-
macOS 原生(Swift)
-
AVFoundation:摄像头采集
-
Vision:手势关键点识别
-
MethodChannel:与 Flutter 通信
-
二、问题现象:程序能编译,但一启动就崩
程序可以正常执行:
flutter run -d macos
但启动后立刻崩溃:
-[hand_gesture_control.AppDelegate applicationDidFinishLaunching:] unrecognized selector sent to instance
特点:
-
Swift 编译正常
-
AppDelegate 文件存在
-
Target Membership 正确
-
DerivedData 多次清理无效
三、根因分析
核心结论一句话
Flutter macOS 项目中,不要 override AppDelegate 的启动生命周期方法。
Flutter macOS 使用的是 FlutterAppDelegate,其内部已经接管了:
-
applicationDidFinishLaunching
-
Flutter Engine 初始化
-
Window / Plugin 生命周期
在此基础上再 override,会导致 Objective‑C selector 在最终二进制中缺失或冲突,从而产生运行时崩溃。
四、错误做法(请全部避免)
❌ override applicationDidFinishLaunching ❌ override applicationDidBecomeActive ❌ override applicationWillTerminate ❌ 在 AppDelegate 中初始化 Camera / Vision / Channel
这些在原生 macOS App 中是对的,但在 Flutter macOS 中是错误的。
五、唯一正确且稳定的解决方案
正确切入点
MainFlutterWindow.awakeFromNib()
这是 Flutter 官方模板中:
-
Flutter engine 已创建
-
FlutterViewController 已就绪
-
Plugin 已注册完成
-
不涉及 ObjC selector 派发
六、最终正确工程结构
1. AppDelegate.swift(最小化)
@main
class AppDelegate: FlutterAppDelegate {
override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
true
}
}
2. MainFlutterWindow.swift(关键)
class MainFlutterWindow: NSWindow {
override func awakeFromNib() {
let flutterViewController = FlutterViewController()
self.contentViewController = flutterViewController
RegisterGeneratedPlugins(registry: flutterViewController)
HandGestureService.shared.wireIfNeeded(
binaryMessenger: flutterViewController.engine.binaryMessenger
)
super.awakeFromNib()
}
}
3. 原生服务单例(推荐)
final class HandGestureService {
static let shared = HandGestureService()
func wireIfNeeded(binaryMessenger: FlutterBinaryMessenger) {
// init camera, vision, channel
}
}
七、经验总结(必看)
-
Flutter macOS ≠ 原生 macOS App
-
不要接管 AppDelegate 生命周期
-
原生初始化放在 MainFlutterWindow
-
selector 问题请直接检查最终二进制
八、结语
这个问题不是代码能力问题,而是 架构认知问题。 一旦理解 Flutter macOS 的真实运行机制,后续所有原生扩展都会非常顺畅。
更多推荐



所有评论(0)