Flutter 修复 window_manager_plus 鸿蒙关闭拦截 Bug:让表单在窗口退出前等待确认
欢迎到 CPF-Flutter 鸿蒙社区 交流适配问题。本篇基于社区的 window_manager_plus 修复鸿蒙关闭拦截问题,修复源码已提交到我的个人仓库 abaoge/window_manager_plus,位于 fix/ohos-window-close 分支,对应提交为 845d15b9。
前言
我在开发 Flutter 鸿蒙电脑应用时,给表单页面接入了 window_manager_plus,想处理一个很常见的操作:表单填到一半,用户误点了窗口关闭按钮。这时候,我希望先弹出一个提示,让用户选择继续编辑还是放弃修改,避免一次误操作把刚填的内容丢掉。
不过,在鸿蒙电脑模拟器上测试该包的 1.0.5-ohos-1.0.0 版本时,我发现开启防关闭后,点系统标题栏的关闭按钮,窗口还是直接退出了,页面没有留下可供操作的确认框。这个看起来不大的问题,让我开始排查该包的鸿蒙关闭适配。
于是,我补齐了这部分实现,并用一个普通表单页面验证修复前后的效果。这篇文章就记录一下这次遇到的问题,以及我是怎样复现、定位和修复它的。
一、使用场景与痛点

图1 使用场景
我关心的是用户点下窗口 X 的那一刻:他是准备结束应用,还是只想暂时收起窗口?当前还有没有需要处理的内容?把这些问题放到桌面应用中,关闭前的判断会出现在很多地方。
| 常见情景 | 示例场景 | 关闭影响 | 优化体验 |
|---|---|---|---|
| 编辑内容尚未保存 | 表单填写 | 未保存内容丢失 | 检查修改,提示保存 |
| 退出还是最小化 | 音视频播放 | 播放停止 | 提供退出/最小化选择 |
| 任务还在执行 | 云盘上传、下载 | 传输中断 | 提示未完成任务 |
| 会话尚未结束 | 视频、语音通话 | 对话中断 | 退出前二次确认 |
| 多个窗口或文档同时打开 | 多窗口应用 | 多窗口同时关闭 | 确认关闭范围 |
表1 常见场景
从用户留存的角度,我更看重减少误退出带来的挫败感。比如注册资料、报名信息已经填了不少,误点关闭后如果只能重填,用户就可能放弃这次操作。让愿意继续的人回到原来的位置,保住已经付出的时间,是关闭确认的实际价值。用户明确选择退出时,也应该顺利退出;没有待处理内容时,则不必多加一道确认。
所以,这个功能在界面上可能只有一个弹窗,却关系到用户刚才的输入、正在做的任务,以及关闭按钮是否符合他的预期。我在本文选择普通表单,是为了把其中最容易复现的情况单独拿出来验证。
上面列的是关闭处理的适用场景。本篇补丁负责让应用在窗口退出前收到关闭请求,并有机会取消这次关闭。 保存内容、最小化、后台执行和会话管理仍需应用另行实现,其中最小化也不等于获得后台保活能力。下文只实测表单的关闭确认与输入保留,不把这些扩展场景算作已经验证的功能。
我接入 window_manager_plus,调用 setPreventClose(true),再用 isPreventClose() 回读,得到的也是 true。可原版在鸿蒙电脑模拟器上,点系统标题栏右上角的 X,窗口仍直接消失,页面没有留下可供操作的确认框。刚填过的文字也随着这次退出丢失。
虽然是小问题,但项目中确实需要这个能力:误点关闭后,还能回到刚才的表单,核对输入、继续填写。对已经接入 window_manager_plus 的应用,这次修复要让原有关闭接口能够拦住鸿蒙系统标题栏的关闭请求。
二、修复前后的效果
2.1 同一份表单的关闭对照
原版来自鸿蒙稳定 tag 1.0.5-ohos-1.0.0。我在电脑模拟器上对照原版与补丁版,分别检查系统标题栏 X、应用内关闭按钮和确认框的处理结果,具体记录见表 2。

图2 修复前效果

图3 修复后效果
这次原版与补丁都使用同一份普通表单、宿主配置和依赖锁文件,原生差异仍只落在包内的 WindowManagerPlus.ets。表 2 和图 8 对应的固定验证中,我把预填标题改为“表单填写示例2”;修复后动图则追加了“!!!”。两次记录都通过修改后的文字核对继续编辑时是否保留输入。
| 用户操作 | 原版稳定 tag | 本次最小补丁 |
|---|---|---|
| 开启防关闭后点系统 X | 窗口关闭,原进程退出 | 出现确认框,窗口与进程保留 |
| 选择继续编辑 | 没有机会显示确认框 | 已修改标题保留,仍是同一个进程 |
| 确认框出现后继续点 X | 原版首次已关闭,没有测试这一项 | 共三次请求,只显示一个确认框 |
| 点击“关闭窗口” | 方法返回,窗口保留,但没有确认通知 | 出现确认框,可以继续编辑 |
| 选择放弃并关闭 | 本轮未测试这一决定路径 | 窗口和进程退出,释放阶段没有重复通知 |
表2 关闭结果
在最开始未修复时,点击“关闭窗口”后,没有确认框;接着点系统标题栏 X,窗口消失。
修复后,选择继续编辑,修改后的标题和内容都保留。切到只读预览再返回编辑,输入仍在;最后选择放弃,窗口消失。
2.2 验证环境与固定版本
先把环境列明,免得读者把手机窗口行为、电脑镜像版本和编译 SDK 混在一起。本次使用的是电脑模拟器,截图中的桌面与系统标题栏也来自该模拟器。
| 项目 | 本次使用值 |
|---|---|
| DevEco Studio | 26.0.0.865 |
| HarmonyOS SDK | 26.0.0.105,API 26,Release |
| Flutter 鸿蒙发行版 | 3.41.9+ohos-1.0.2 |
| Dart | 3.11.5 |
| 原版插件 | window_manager_plus 1.0.5,鸿蒙 tag 1.0.5-ohos-1.0.0 |
| 原版固定提交 | 9c47e819888a93443f5e84e880a2723c274dc414 |
| 修复固定提交 | 845d15b96727ddf343f3d365d5d2e9ad1a514b63,分支 fix/ohos-window-close |
| 模拟器 | MateBook Pro ,设备类型 2in1 |
| 模拟器镜像版本 | 7.0.0.107(SP8DEVC00E47R1P3) |
| 设备实际回报的系统版本 | OpenHarmony-7.0.0.105 |
| 屏幕信息依赖 | screen_retriever 0.1.9,固定提交 24162d7e01d3d5f51f076302d830f5e77e34030f |
| 构建方式 | unsigned debug HAP,安装到上述模拟器 |
表3 验证环境
三、原理与核心代码
3.1 为什么继续修这个已有的包

图4 社区仓库
动手之前,我查了 oh-Flutter、CPF-Flutter 和 hxa-flutter 的仓库,并读了命中候选的鸿蒙源码。已有的 flutter_window_close 提供了鸿蒙实现,当前核对到的鸿蒙 tag 是 1.3.0-ohos-1.0.0-beta.1,属于 Beta,其源码已经接入 windowWillClose。不过,我日常使用的是 window_manager_plus,这次没有运行这个替代库,只核对了相关源码。
所以,我继续修复 window_manager_plus,是为了修好它已经公开的关闭接口。对已经依赖这个包的应用,补丁可以保留原有 Dart 调用方式。
还有一个容易写过头的地方。通用 README 和 Dart 接口注释描述了关闭前确认的用法,但鸿蒙专项能力表没有单独承诺这项系统按钮行为。我不能把通用描述改写成“鸿蒙功能清单明确宣称已经完成”。我的修复依据是已有接口、实际鸿蒙调用链和原版复现结果,三者需要一起看。
为了排除“旧 tag 已经修好”的情况,我还检查了默认分支和更新开发分支。三者在其他窗口方法上有差异,但都没有注册系统预关闭事件,关闭通知仍出现在释放路径。因此我将固定稳定版作为基线,同时把查到的当前实现情况写进了选题记录。
3.2 问题出在关闭请求抵达的时间
我最先检查的是两个入口。第一个入口是页面按钮调用 WindowManagerPlus.current.close(),它通过 MethodChannel 进入插件的 close 分支;第二个入口是用户点击系统标题栏 X,它由鸿蒙窗口系统处理。两个入口看起来都在关窗口,却不会自动经过同一段代码。
原版 setPreventClose 只把传入值存到 isPreventClose 字段,随后返回成功。原版 close 分支会读这个字段:允许关闭时调用销毁;防关闭开启时返回 false。可是这个分支既没有通知 Dart,也没有为系统按钮注册监听。于是页面按钮只是“不关了”,系统按钮则完全没有经过这个判断。
这解释了两个表面相反的现象。应用内按钮按了没有反应,不能算关闭确认正常,因为页面没有收到请求;系统 X 一点就退出,也不能靠再设置一次布尔值修好,因为系统没有订阅者可以询问。回读为 true 只能证明字段已被写入,不能替代一次真实系统操作。
我还区分了方法返回与用户决定。Dart 的 close() 返回类型是 Future<void>,等待它完成,只说明这次通道调用已经结束,不能据此判断用户选择了放弃。若在按钮回调的 await 后立即清空备注,即便窗口成功留下,输入也会被应用自己的代码删掉。退出决定应留在确认回调里处理,页面按钮只负责提出请求。
原版确实有 onWindowClose,但我沿调用链找到的是:宿主进入 onWindowStageWillDestroy,调用 releaseWindowManager,再由管理对象的 release() 发出 close 事件。这个时候系统已经走到窗口销毁流程。Dart 收到的更像释放通知,已经无法把用户停在输入页上等确认。

图5 日志节选
日志把时间关系保留下来了:原版先出现 releaseWindowManager:0,紧接着是 onWindowClose,然后才看到 Dart 的 request=1。即使日志里的 mounted=true,也不表示窗口还拥有取消销毁的机会。Widget 对象是否仍挂载,与系统是否已经开始关闭,是两个不同层面的状态。
所以我没有往 Dart 多加一次延时,也没有让确认框提前常驻。要修的地方是鸿蒙的预关闭事件:在窗口真正消失之前接住请求,并向系统返回本次是否继续关闭。
3.3 宿主装配与防关闭初始化
这个包的鸿蒙实现依赖宿主提供 WindowStage。我沿用原包 Example 的接法:EntryAbility 登记实际创建的 stage,Dart 初始化时由插件取得主窗口。下面这张流程示意图把宿主、页面监听和防关闭设置串起来;如果连主窗口都没取得,就还没走到本篇要修复的关闭监听。

图6 初始化流程
下面是宿主文件 EntryAbility.ets 中的完整创建回调,它属于 EntryAbility 类。我只展示与装配有关的完整函数,不展开该包的多窗口功能。
onWindowStageCreate(windowStage: window.WindowStage): void {
super.onWindowStageCreate(windowStage);
if (this.windowName) {
// 把宿主实际创建的 stage 交给原包,不能另造一个窗口对象。
WindowStageManager.addStage(this.windowName, windowStage);
}
}
本次使用默认窗口 ID 0。宿主还负责 Flutter 插件注册、入口传参与生命周期清理。页面监听要在开启防关闭前接好,否则原生即使取消了关闭,Dart 也可能没有接收事件的对象。
我在配置期间禁用页面的“关闭窗口”按钮,回读成功后才开放;失败则显示错误与重试。guard=true 只说明保护状态已写入,能否拦住系统 X 仍要实际点击。 图中的错误分支对应下面的 configure(),不涵盖 main() 中 ensureInitialized 的所有失败。
Dart 页面 lib/main.dart 使用下面这个完整函数,代码与当前 Example 一致。
Future<void> configure() async {
try {
// 使用原库公开接口,不让页面自己接管鸿蒙窗口事件。
await WindowManagerPlus.current.setPreventClose(true);
final enabled = await WindowManagerPlus.current.isPreventClose();
debugPrint('CLOSE_PROBE guard=$enabled');
if (mounted) {
setState(() {
ready = enabled;
errorMessage = enabled ? null : '关闭保护未能开启,请先保留你的修改,再重试。';
});
}
} catch (error) {
debugPrint('CLOSE_PROBE setup_failed=${error.runtimeType}');
if (mounted) {
setState(() => errorMessage = '窗口关闭保护初始化失败,请重试。');
}
}
}
本例只使用应用自己的窗口事件,没有新增文件访问、全局输入监听或预退出权限。表单内容只保存在内存里,没有存盘操作,也就没有文件授权步骤。
3.4 原生先回答系统:这次关闭是否取消
我选择的公开接口是 window.Window.on('windowWillClose')。它从 API 15 起提供,回调返回 Promise<boolean>。这个布尔值的方向非常容易看反:resolve true 表示取消本次关闭,resolve false 或 reject 表示继续关闭。
Dart 弹窗里的 true 却表示“用户同意放弃并退出”,不能原样传给系统。我把两者分开:原生按已有防关闭状态回答系统,Dart 异步处理用户选择。下面的泳道图只画本例已开启保护时,点击系统 X 的路径。

图7 关闭处理流程
补丁保存一个固定的回调成员,后续注册和注销都使用同一个函数引用。下面是完整成员初始化与两个核心函数;它们位于 WindowManagerPlus.ets 的类体中。
private hasWindowWillCloseListener: boolean = false;
private windowWillCloseCallback: () => Promise<boolean> = (): Promise<boolean> => {
// reject 会被系统视为继续关闭,因此这里不等待 Dart 弹窗的决定。
return Promise.resolve(this.handleCloseRequest());
};
private registerWindowCloseListener(): void {
if (this.hasWindowWillCloseListener) {
return;
}
const currentWindow = this.windowClass;
if (currentWindow === null) {
throw new Error('Cannot prevent close: window is not available');
}
// 注册成功之后才记录状态,供后续设置与注销复用。
currentWindow.on('windowWillClose', this.windowWillCloseCallback);
this.hasWindowWillCloseListener = true;
}
private handleCloseRequest(): boolean {
const preventClose = this.isPreventClose;
try {
// 系统 X 与应用 close() 共用通知,弹窗内容仍由 Dart 决定。
this.onWindowClose();
} catch (exception) {
const error = exception as BusinessError;
console.error(`Failed to notify close request. Cause code: ${error.code}, message: ${error.message}`);
}
// 通知失败也不能把已经开启的拦截变成放行。
return preventClose;
}
对照图和代码,我主要检查三个地方:
- 先取值,再通知,再返回。
handleCloseRequest保存当前防关闭快照,发送事件后立即返回,不等待用户选择。窗口没有被这次请求关闭,继续编辑只需收起弹窗,输入控制器也不必重建。 - 确认退出后调用
destroy()。close()仍是一次允许拦截的关闭请求。用户已选择放弃,再调用它会重复确认;明确退出时才走destroy()。应用内的close()也复用请求处理,但它不经过图中的系统Promise<boolean>回调。 - 通知失败不自动放行。
catch保留原生错误上下文,返回值仍是先前的快照。它只处理本次通知同步抛出的异常;Dart 没有监听、事件无法抵达等情况,不能算作已经兜底,应用也不会因此自动显示确认框。
注册过程还有一个区别:窗口初始化时,首次注册监听的异常只记录日志;调用 setPreventClose(true) 时会再次检查,必要时重试注册。只有注册未抛错才写入保护状态,否则把 SDK 错误交给 Dart。因此,ensureInitialized 返回本身不能证明监听已经接好。
设备边界:
windowWillClose受自由窗口状态限制,不适用时可能不报错却不生效。我仍以电脑模拟器中实际点击系统 X 的结果为准;注册失败和不支持设备的路径尚未做运行对照。
3.5 Dart 负责让用户决定
收到 close 事件之后,页面要做的事情很直接:如果没有确认框,就显示一个;用户选择继续编辑,不调用退出;选择放弃,再主动销毁窗口。下面是完整回调,和本次原版、补丁版使用的是同一份 Dart 逻辑。
void onWindowClose([int? windowId]) async {
requests += 1;
debugPrint('CLOSE_PROBE request=$requests mounted=$mounted');
// 原生可能重复通知;页面保证同一时刻只有一个确认框。
if (!mounted || dialogOpen) return;
dialogOpen = true;
try {
final close = await showDialog<bool>(
context: context,
barrierDismissible: false,
barrierColor: const Color(0x63203229),
builder: (context) => const CloseFormDialog(),
);
debugPrint('CLOSE_PROBE decision=${close == true ? "discard" : "keep"}');
// 继续编辑只收起弹窗,只有明确放弃才主动销毁窗口。
if (close == true) await WindowManagerPlus.current.destroy();
} catch (error) {
debugPrint('CLOSE_PROBE close_failed=${error.runtimeType}');
if (mounted) {
setState(() => errorMessage = '关闭处理失败,当前修改仍保留,请重试。');
}
} finally {
dialogOpen = false;
}
}
CloseFormDialog 是同一文件中的完整 Widget。它询问“放弃未保存的内容?”,明确说明关闭后本次填写的内容会丢失,把“继续编辑”作为主操作,“放弃并关闭”作为退出选择;两个按钮分别通过 Navigator.pop 返回 false 和 true。预览状态下关闭也使用这个确认框,切换预览并没有提交或保存表单。
我把 dialogOpen 放在第一次 await 之前设置。否则用户连续点 X,前一个异步回调还没显示完弹窗,下一个回调就可能继续创建新弹窗。这里使用的是页面本地状态,和“到底收到多少次请求”分开:计数照常增加,重复弹窗才被挡住,日志因而能看见真实输入。
本轮连续点击三次,日志出现 request 一到三,界面只有一个确认框。这个结果应归因于原生拦截与页面已有去重共同工作。因为两版 Dart 完全相同,我没有把 dialogOpen 写成补丁新增加的功能,更没有把它塞进窗口插件,让库去判断某个业务弹窗是否打开。
重复请求也没有替用户累计成一次“强制同意”。页面会记录它们,但已有确认框还在时直接返回,不追加新的退出任务。等用户选择继续编辑,finally 才把去重状态复位,下一次独立关闭请求就能再次询问。如果把这个标志永久保持为 true,第一次取消后再点关闭便会没有任何反馈,所以它的复位时机同样属于页面职责。
选择继续编辑后,标题仍是“表单填写示例2”,内容也保留,进程仍为 5287,窗口没有关闭重建。重新启动会加载预填的“表单填写示例”;这是 Example 的初始值,不能算恢复了本轮修改。

图8 继续编辑
Example 从预填示例开始,输入只保留在当前窗口。这里统一启用防关闭,并不根据字段是否为空、是否恢复原文来开关保护。状态文字说明用户正在做什么,实际退出仍交给同一套插件接口。业务应用可以根据自己的保存记录决定何时提示,但这不属于本次鸿蒙补丁。
3.6 release 只做清理,不再发一次关闭请求
接上预关闭事件之后,原版 release() 中那次 onWindowClose() 就必须重新审视。用户已经在确认框里选择放弃,Dart 也已经调用销毁,如果释放时再发一条 close,请求又回到页面,就可能重复弹窗或者在正在消失的界面上处理新的确认。
我删除的就是这次释放阶段的通知。它不该承担“请用户决定是否退出”的职责,因为释放已经是决定之后的清理。下面是修改后的完整函数,原包其他窗口监听的清理逻辑保留,新增关闭监听则按实例的回调引用注销。
public release() {
// release 已处于销毁清理,不重复发出可以取消的 close 请求。
this.isPreventClose = false;
if (this.hasWindowWillCloseListener) {
try {
// 只注销本实例添加的关闭监听,不移除宿主的同类监听。
this.windowClass?.off('windowWillClose', this.windowWillCloseCallback);
this.hasWindowWillCloseListener = false;
} catch (exception) {
const error = exception as BusinessError;
console.error(`Failed to unregister close callback. Cause code: ${error.code}, message: ${error.message}`);
}
}
this.channel?.setMethodCallHandler(null);
try {
this.windowClass?.off('windowEvent');
this.windowClass?.off('windowStatusChange');
this.windowClass?.off('windowRectChange');
} catch (exception) {
console.error(`Failed to unregister callback. Cause code: ${exception.code}, message: ${exception.message}`);
}
}
对应的 Dart 页面在 dispose 中移除监听器、释放文本控制器,这样页面结束后不会继续把自己留在事件接收列表里。原生与 Dart 各自清理自己注册的对象,不依赖“进程反正马上会退出”来掩盖生命周期问题。
补丁日志的末尾是用户决定 discard,随后出现 releaseWindowManager:0,之后没有额外的 CLOSE_PROBE request。我还确认窗口消失、PID 查询为空,才把放弃退出记为通过。只看方法返回成功,无法证明窗口已经退出;只看桌面空了,也无法证明没有多余回调。
这次变化也会影响错误使用旧事件时机的代码。如果某个消费方一直把 close 当成“销毁完成通知”,现在应重新检查用途。它在公开接口中的含义是关闭请求,应用不能把用户取消的请求记成已经退出,更不能在每次请求到达时立刻丢弃输入。
四、踩坑与边界条件
4.1 复现中确认的问题与接入时要注意的地方
回过头看,这次最容易误判的是把接口回执当成了用户操作结果。我把已复现的问题和代码接入时的注意点分开列,后者没有另做故障注入,不能当成新增实测。
表4 接入注意
| 类型 | 容易误判的地方 | 我的处理方式 |
|---|---|---|
| 原版已复现 | isPreventClose() 返回 true,就认为系统 X 已受控 | 必须实际点系统标题栏 X,核对窗口、弹窗和进程 |
| 原版已复现 | 应用内 close() 已返回,就认为用户已收到确认 | 同时检查 Dart 是否收到请求,页面是否留下可操作的确认框 |
| 接入注意 | 把系统回调和弹窗结果中的 true 当成同一个意思 | 系统 true 是取消关闭;本例弹窗 true 是明确放弃,分别处理 |
| 接入注意 | 确认放弃后再调用 close() | 放弃分支调用 destroy(),避免再次提出同一个关闭请求 |
| 补丁已验证 | 连续点 X 时,只看请求数,不看弹窗数 | 保留请求日志,用页面的 dialogOpen 去重;本轮三次请求只有一个框 |
| 接入注意 | 看到重启后又出现预填内容,就当成恢复了草稿 | 区分固定预填内容与本轮编辑结果;当前 Example 没有存盘 |
4.2 已测范围与未验证的条件
这次实测覆盖的是电脑自由窗口下的主窗口与系统标题栏 X。官方接口说明,这个预关闭监听并不会覆盖所有关闭方式。系统强杀、崩溃和断电没有机会给页面慢慢询问,Dock、托盘或快捷键等入口也不能从本轮结果直接推断。
我没有扩大到 UIAbility 的预退出钩子。那是另一个层级,涉及自己的权限、设备条件、回调优先级及超时约束;把它与窗口关闭监听同时接入,需要重新梳理谁先处理退出。本篇只修现有库的一个明确入口,避免用更大范围的退出接管掩盖原本的缺口。
表5 验证边界
| 范围 | 本文可以确认的内容 |
|---|---|
| 继续编辑 | 本轮同一进程内,原输入保留,页面可继续使用 |
| 放弃并关闭 | 本轮窗口和进程退出,释放阶段没有重复通知 |
| 关闭请求重复到达 | 本轮三次请求,页面已有去重只显示一个框 |
| 防关闭设为 false | 实现保留该状态分支,尚未做运行对照 |
| 注册失败、能力不支持 | 有错误传递逻辑,尚未做对应环境实测 |
| 多窗口、全屏、真机 | 本轮未验证 |
| 保存与恢复 | Example 未实现保存业务,不能据此声称防丢数据或重启恢复 |
五、接入与复用建议
5.1 接入自己的项目
我在接入时主要检查三件事,保存规则和确认文案仍由应用决定:
- 初始化顺序。 按第 3.3 节装配宿主,先注册 Dart 监听,再调用
setPreventClose(true)并回读;失败时显示错误,不把保护状态标为已开启。 - 退出决定。 继续编辑只收起弹窗,明确放弃才调用
destroy()。需要保存时,先保存成功再退出,失败则保留窗口。 - 资源清理。 页面释放时移除监听器和文本控制器,避免已结束的页面继续接收事件。
5.2 取得源码与验收步骤
我把修复放在个人仓库的 fix/ohos-window-close 分支。复现时固定到本文对应的提交:
git clone --branch fix/ohos-window-close https://atomgit.com/abaoge/window_manager_plus.git
cd window_manager_plus
git checkout --detach 845d15b96727ddf343f3d365d5d2e9ad1a514b63
已有项目可将插件的 Git 依赖指向该仓库和提交,保留原有依赖锁文件。仓库保留上游 example/,不含截图中的普通表单验证工程;补丁尚未合入原社区仓库,也未发布新的包版本。
接入后,我按下面四步验收,系统标题栏 X 和应用内按钮都要检查:
- 在 HiLog 中确认
guard=true,修改表单标题。 - 连续点三次系统 X,确认只出现一个弹窗;选择继续编辑,检查输入和 PID 均未改变。
- 点击应用内“关闭窗口”,确认仍可取消;再发起关闭并选择放弃。
- 确认窗口消失、PID 查询为空,释放阶段没有新增关闭请求。
本次两版表单均完成了无签名 debug HAP 构建、模拟器运行和页面范围的 flutter analyze lib 检查;整仓静态检查未全部通过。无签名构建仅用于上述模拟器,真机需另行配置签名。
下面两图来自早期最小 Probe。普通表单沿用其 2in1、multiton 和空权限配置;日志用于说明原版先释放、后通知的顺序,不代表新版 UI 或进程 PID。

图9 早期宿主配置

图10 早期关闭日志
小结
现在可以说清楚这次升级的用途:已经使用 window_manager_plus 的鸿蒙电脑编辑器,开启防关闭后,用户点击系统 X,会先进入原有 Dart 确认逻辑。用户选择继续编辑,窗口和内存输入留下;选择放弃,再主动退出。应用内关闭按钮也走同一套请求通知。
真正需要改的鸿蒙部分,是系统预关闭事件的注册、布尔返回值、通道通知时机和释放清理。确认框本来就在 Flutter 页面里,修复让它终于有机会在正确的时候出现。原版与补丁的同条件结果已经验证了这一点,剩余设备状态和失败路径则继续按未测项保留。
参考资料
更多推荐


所有评论(0)