三层排查实锤(lldb 运行时取证): 1. 红点按钮真实 action 是 AppKit 私有 selector `_close:`(并非文档所称 performClose:),内部直接关窗,绕过 performClose/close 重写与 delegate 拦截 2. flutter create 模板的 AppDelegate 自带 applicationShouldTerminateAfterLastWindowClosed 返回 true——最后一个窗口 关闭即 NSApp.terminate,这是"点红点进程退出、托盘随进程消失"的根因 3. 修复:改为返回 false。窗口即便被真关闭,进程/HTTP 服务/托盘也保持运行, 托盘点击 makeKeyAndOrderFront 恢复窗口(releasedWhenClosed=NO,窗口对象在) 验证(红点 action 原样重放 `_close:`):窗口关闭后进程存活、服务在线、 托盘状态栏窗口存在、makeKeyAndOrderFront 可恢复窗口。 保留 MainFlutterWindow 的 performClose/close 重写(Cmd+W 等正常路径只隐藏 不关闭,体验更好)。
32 lines
1.2 KiB
Swift
32 lines
1.2 KiB
Swift
import Cocoa
|
||
import FlutterMacOS
|
||
|
||
class MainFlutterWindow: NSWindow {
|
||
override func awakeFromNib() {
|
||
let flutterViewController = FlutterViewController()
|
||
let windowFrame = self.frame
|
||
self.contentViewController = flutterViewController
|
||
self.setFrame(windowFrame, display: true)
|
||
self.minSize = NSSize(width: 860, height: 560)
|
||
|
||
RegisterGeneratedPlugins(registry: flutterViewController)
|
||
|
||
super.awakeFromNib()
|
||
}
|
||
|
||
/// 关窗不退出(最终拦截点)。实测(macOS 15)红点按钮的真实 action 是 AppKit
|
||
/// 私有 selector `_close:`(并非文档所称的 performClose:),其内部直接调 close(),
|
||
/// 会绕过 performClose/windowShouldClose/delegate 拦截——因此必须在 close() 上拦。
|
||
/// 此处 orderOut 隐藏窗口,进程/HTTP 服务/托盘保持运行;NSApp.terminate 流程中
|
||
/// 的窗口关闭不受影响(进程照常结束),真正退出只走托盘菜单。
|
||
override func close() {
|
||
print("MainFlutterWindow: close() 拦截,窗口隐藏到托盘(服务继续运行)")
|
||
orderOut(nil)
|
||
}
|
||
|
||
/// Cmd+W / 菜单"关闭窗口"走 performClose:,同样只隐藏不关闭。
|
||
override func performClose(_ sender: Any?) {
|
||
orderOut(sender)
|
||
}
|
||
}
|