Pure Live Windows 小窗几何捕获与重置链路深度解析:显示器 ID 持久化与事务门禁的修复实践
发布时间:2026/10/15 8:15:48 锦皓数字建站

音视频直播移动开发【免费下载链接】pure_live纯粹直播:哔哩哔哩/虎牙/斗鱼/快手/抖音/网易cc/YY直播/Twitch直播/SOOP直播/M38自定义源应有尽有。项目地址https://gitcode.com/gh_mirrors/pur/pure_live点击查看免费下载导读本文以 Pure Live纯粹直播维护分支中针对 Windows 桌面小窗画中画 PiP的一次专项审计为线索完整剖析原生窗口几何捕获 → 显示器身份持久化 → 备份导出 → 视频设置页手动重置这条链路的两处相邻根因及其修复方案。读完本文你将理解WindowPipGeometry嵌套快照的归一化契约、toJson()与update()之间结构错配如何导致显示器 ID 被静默丢弃、备份导出为何要保留旧别名以及设置页重置确认框如何通过同步事务门禁避免连续触发叠加弹窗并掌握最终的验证方法与验收边界。审计范围与结论概览本次复核针对 Windows 小窗从原生窗口几何捕获、显示器身份持久化、备份导出到视频设置页手动重置的完整链路基线提交为806c5b8b401afea263dcfb456618edac4fc366be实现提交为b13d8dea6d0b54ba2ec0de5f461d2149279f925c。审查确认了两个相邻的根因运行时捕获与导出的结构错配WindowPipGeometry.update()和toJson()把displayId放在顶层与尺寸、坐标平级而归一化器只从当前嵌套结构windowsPip.displayId或旧别名windowsPipDisplayId读取它。结果是尺寸与坐标被正确保留显示器 ID 却稳定地变成空字符串。空 ID 在播放器里被当作任意显示器均匹配主窗口换屏后可能继续采用另一块屏幕保存的小窗边界备份也因此失去精确的显示器身份。设置页重置入口缺少页面事务门禁旧实现直接创建普通AlertDialog同一回调连续触发会叠加两个确认路由在 320×480、3.0 倍英文文字下每个弹窗均向下溢出 704 px且动作按钮仍使用含义较弱的通用确认文案。修复方向很明确将运行时捕获与导出统一为同一个规范嵌套快照显示器 ID 裁剪首尾空白后与尺寸、坐标一起保存重置入口改为独立有状态组件在创建路由前同步取得单次事务所有权并在取消、遮罩、系统返回、确认或页面销毁后统一释放确认后核对页面与窗口设置控制器生命周期一次性清空显示器 ID、宽、高、横坐标和纵坐标五个字段并显示双语完成反馈。数据模型规范嵌套快照的契约WindowPipGeometry 的字段结构小窗几何的核心数据模型位于 lib/core/config/window_size_controller.dartWindowPipGeometry持有横屏与竖屏两套几何每套都包含显示器 ID、宽、高、横纵坐标字段Hive 键说明displayIdwindows_pip_display_id小窗最后所在显示器的 IDwindowsPipWidthwindows_pip_width小窗宽度windowsPipHeightwindows_pip_height小窗高度windowsPipX/windowsPipYwindows_pip_x/windows_pip_y小窗横/纵坐标portraitDisplayId等windows_pip_portrait_*竖屏视频对应的一套几何所有字段都是基于 Hive 的 Rx 值写入即持久化。有效性判定isValid严格要求显示器 ID 非空去除首尾空白后且尺寸坐标有限且宽高大于 0hasValidBoundsbool get isValid { return displayId.v.trim().isNotEmpty hasValidBounds; }也就是说显示器 ID 是小窗几何有效的硬前提——这正是第一个根因危害最大的地方一旦 ID 丢失尺寸坐标即使完好几何也处于半有效状态。统一入口update()与归一化器update(Size, Offset, String)是运行时捕获写入的唯一入口window_size_controller.dart竖屏对应updatePortrait。它们先做防御性校验尺寸/坐标有限、非空、显示器 ID 去除空白后非空再构造嵌套结构交给WindowSizeController.normalizePipGeometry归一化后写入void update(Size size, Offset position, String displayId) { if (!size.isFinite || size.isEmpty || !position.isFinite || displayId.trim().isEmpty) { return; } _assign( WindowSizeController.normalizePipGeometry({ windowsPip: { displayId: displayId, windowsPipWidth: size.width, ... }, }), ); }归一化器_normalizePipRectwindow_size_controller.dart是整条链路的关键裁决点它按优先级读取显示器 ID嵌套结构的pip[displayId]旧式嵌套别名竖屏时为legacy[portraitDisplayId]顶层旧别名windowSize[windowsPipDisplayId]。随后对宽高做0.0 ~ maxWindowDimension(16384)的 clamp并始终displayId.trim()去除首尾空白后输出规范快照。注意它的取值对象windowSize必须包含windowsPip这一嵌套段否则读不到嵌套 ID。两个根因结构错配让 ID 静默蒸发修复前的toJson()是这样写的MapString, dynamic toJson() { return WindowSizeController.normalizePipGeometry({ displayId: displayId.v, // ← 顶层而非嵌套段内 windowsPipWidth: windowsPipWidth.v, ... }); }displayId被放在顶层与windowsPipWidth等平级而_normalizePipRect只从windowSize[windowsPip][displayId]或windowSize[windowsPipDisplayId]读取。于是toJson()产出的快照里displayId永远读不到 → 归一化为_assign再把空串写回displayId.v。一次导出/自修复即可永久清空显示器 ID而宽、高、坐标因为字段名恰好匹配而幸存形成尺寸正确、ID 为空的隐蔽状态。这一点在审计红灯中体现得十分典型首轮弹窗修订后同一用例继续以 4 PASS / 1 FAIL 暴露捕获后显示器 ID 为空修正update()后联合记录 9 PASS / 1 FAIL 进一步精确定位到toJson()再次丢弃显示器 ID两个结构错配分别修订且没有用放宽断言来掩盖——这是审计方法论上值得注意的一点。修复后的统一嵌套快照修复后toJson()与update()使用完全相同的嵌套形状window_size_controller.dartMapString, dynamic toJson() { return WindowSizeController.normalizePipGeometry({ windowsPip: { displayId: displayId.v, windowsPipWidth: windowsPipWidth.v, windowsPipHeight: windowsPipHeight.v, windowsPipX: windowsPipX.v, windowsPipY: windowsPipY.v, }, }); }运行时捕获与导出从此共享同一个规范快照形状显示器 ID 裁剪首尾空白后与尺寸、坐标一起保存不再有第二条分叉路径。备份导出的兼容性保留旧别名而非降级小窗几何随备份导入导出其序列化/反序列化由WindowSizeController承担toJson()将windowsPip.toJson()与windowsPip.portraitToJson()收进windowSize段window_size_controller.dartfromJson()/parseConfig()以strict: true走normalizePipGeometry遇到非 Map 的嵌套段或非法坐标会抛FormatExceptionwindow_size_controller.dart保证坏数据不会静默写入extractConfig()导出时在规范嵌套结构之外同时保留windowsPipDisplayId、windowsPipWidth等旧顶层别名window_size_controller.dart源码注释明确说明其目的保留旧别名使老版本备份编辑器与降级导入能无损还原矩形mergeConfig()导入合并时检测到旧别名键即触发归一化并把别名映射回规范嵌套段window_size_controller.dart。这套规范结构 兼容别名的设计正是交互合同第 2 条当前备份导出保留同一显示器身份不把规范字段误降级为空字符串或旧别名的落地新版本导出的是嵌套规范快照旧别名只是供旧工具无损读取的附加投影二者来源相同不会互相打架。备份控制器BackupController在导出时直接取WindowSizeController.toJson()在导入时经parseConfig(extractConfig(data))还原lib/features/backup/backup_controller.dart、lib/features/backup/backup_controller.dart因此小窗几何的显示器身份会随备份完整往返。播放端如何消费显示器 ID小窗驱动 lib/core/player/presentation/windows_pip_driver.dart 是几何数据的消费端。DisplayAwarePipWindow通过三个回调接入控制器_readWorkAreas()用screenRetriever.getAllDisplays()枚举所有显示器把display.id.toString()作为工作区 IDwindows_pip_driver.dart_readSavedBounds()仅在rememberPipPosition开启时读取按当前视频是否竖屏windowsPipShowsPortraitVideo分别取横屏/竖屏几何ID 与矩形一起返回windows_pip_driver.dart_writeSavedBounds()小窗拖动/缩放后回调pip.update(...)写入windows_pip_driver.dart。显示器 ID 在播放器侧的意义是恢复小窗时先匹配保存时所在的显示器只有目标显示器不可用时才回退到当前主屏。若 ID 为空保存的矩形会被当作任意显示器均适用主窗口换屏后小窗就可能落在另一块屏的旧坐标上——这正是审计要消除的体验问题。驱动还通过lifecycleHooks在进出小窗时打印几何诊断日志beforeEnter记录锁宽高比、最小尺寸、基准尺寸afterEnter对比窗口 bounds 与 Flutter 布局视图用于区分窗口不断弹回视频形状与地板尺寸不是设置值这两类难以从外部分辨的问题windows_pip_driver.dart。重置入口有状态组件 同步事务门禁为什么旧实现会叠加两个路由旧的重置入口直接创建AlertDialog没有页面事务门禁同一回调连续触发会在根 Navigator 上叠加两个确认路由。用户连点重置或动画未完成时再次触发会看到两个弹窗且每个都在小屏320×480、3.0 倍缩放英文文案下向下溢出 704 px——这是审计记录里首个 FAIL 的确切现场。修复后的实现形态修复后的重置入口是独立的有状态组件_WindowsPipResetTilelib/features/settings/pages/video_settings_page.dart挂在 Windows 平台分支末尾if (_isWindows) const _WindowsPipResetTile()见 video_settings_page.dart。关键点同步门禁_resetBusy在await showDialog之前setState置位onTap: _resetBusy ? null : _confirmReset使弹窗展示期间入口整体禁用回调自身开头也有if (_resetBusy || !mounted) return;双保险finally中统一复位保证取消、遮罩、系统返回、确认或页面销毁后都能重新进入。生命周期核对确认返回true后再检查mounted与window.isClosed窗口设置控制器已退出则不提交随后一次性执行清理if (confirmed ! true || !mounted || window.isClosed) return; window.clearWindowsPipGeometry(); // 清空五个字段含竖屏套 SettingsService.to.player.floatWindowGeometry.value ; // 同步清空浮窗几何串 ToastUtil.show(i18n(windows_pip_reset_position_success));clearWindowsPipGeometry()委托给WindowPipGeometry.clear()window_size_controller.dart将displayId、宽、高、横纵坐标五个字段连同竖屏套一起归零避免留下只有尺寸、没有 ID或只有 ID、没有尺寸的部分矩形被误判为有效。统一确认框规范使用根 NavigatoruseRootNavigator: true、16/20 窗口边距、420 px 正文宽度上限BoxConstraints(maxWidth: 420)、scrollable: true滚动正文与actionsOverflowDirection: VerticalDirection.down动作向下溢出取消按钮与红色FilledButtoncolorScheme.error背景最小尺寸均为 48×48在 320×480、3.0 倍英文文字下保持完整可见与可命中。语言文案重置相关的四组文案在 assets/translations/zh.json 中成对出现英文对应 assets/translations/en.json并明确重置后从应用所在显示器右下角打开这一默认落点windows_pip_reset_position重置小窗位置和大小windows_pip_reset_position_subtitle清除已保存的小窗尺寸与位置下次从应用所在显示器右下角打开windows_pip_reset_position_confirm确定要清除已保存的小窗位置和大小吗windows_pip_reset_position_success已清除保存的小窗位置和大小数据与交互合同六条验收基准本批修复以六条数据与交互合同作为验收基准也是文章读者可以自行核验的行为契约有效的运行时捕获把显示器 ID、尺寸和位置作为一个规范快照提交显示器 ID 去除首尾空白。当前备份导出保留同一显示器身份不把规范字段误降级为空字符串或旧别名。重置确认出现前后同一页面只持有一个事务快速重复触发只创建一个路由。取消、点击遮罩或系统返回不修改任何已存几何门禁随后允许重新进入。确认只提交一次并同时清空五个字段避免留下可被误判为有效的部分矩形。页面或窗口设置控制器退出后不提交旧页面动作也不发布迟到反馈。第 3、5、6 条分别对应事务门禁、五字段原子清空与生命周期核对第 1、2 条对应嵌套快照统一第 4 条保证用户反悔路径无损。质量门禁与最终验证审计采用分阶段的红灯驱动验证全部构建记录位于local-artifacts/build-records/阶段记录文件结果设置页旧实现20260914T025057366Z-quality-focused.json4 PASS / 1 FAIL连续触发创建两个弹窗各报告 704 px 底部溢出弹窗修订后20260914T025238831Z-quality-focused.json4 PASS / 1 FAIL捕获后显示器 ID 为空控制器修订后20260914T025419531Z-quality-focused.json4 PASS / 1 FAIL有效尺寸坐标写入后isValid仍为 falseupdate()修正后20260914T025519425Z-quality-focused.json9 PASS / 1 FAIL定位到toJson()再次丢弃 ID初步联合20260914T025625521Z-quality-focused.json设置页与窗口边界 10/10 PASS最终20260914T025833852Z-quality-focused.json五文件联合 31/31 PASS最终记录覆盖的五个文件按文档记录包括设置页分辨率对话框、窗口尺寸边界、小窗几何、备份导入校验与翻译契约测试。此外最后一次 Dart 编辑后的全库flutter analyze为No issues found耗时 46.3 秒构建策略静态检查、仓库完整性审计及git diff --check均通过仓库审计为 0 error。**每个红灯逐一击破、用断言而非放宽断言来定位**是本批审计最值得借鉴的工程方法。验收边界与后续候选本批为 W1-01 与 W2-01 增加了源码/Widget/持久化证据两组继续保持RUN宏观账本维持 20 PASS / 42 RUN / 0 NR共 42 组未闭环。本批没有启动 Windows 原生候选、移动真实小窗、构建安装包、操作手机或发布 3.2.0。后续 Windows 候选需在主副屏之间分别验证首次打开、拖动与缩放、退出再进、小窗期间切屏、主窗口换屏后的显示器匹配、取消/系统返回/连续点击、确认后默认右下角位置以及进程重开和备份往返。普通窗口尺寸与 PiP 矩形的窗口事件分流、宿主队列排序及最小化/最大化/真全屏隔离已由配套的 Windows 窗口几何捕获所有权审计 补齐真实窗口往返按同一累计候选验证。对正在阅读此文的开发者而言若你想在自己的 Flutter Windows 应用里实现小窗位置记忆 显示器感知恢复 设置页安全重置本文给出的三层要点可以直接复用捕获与导出共用同一规范嵌套快照、归一化器作为唯一裁决点统一收敛 ID 与边界、确认类操作以同步门禁 生命周期核对保证单事务。赞分享音视频直播移动开发【免费下载链接】pure_live纯粹直播:哔哩哔哩/虎牙/斗鱼/快手/抖音/网易cc/YY直播/Twitch直播/SOOP直播/M38自定义源应有尽有。项目地址https://gitcode.com/gh_mirrors/pur/pure_live点击查看免费下载相关推荐Pure Live 小窗弹幕设置页原生审计基于 ADB 语义化路由与重启持久化的自动化验收实践Pure Live 小窗弹幕设置页原生审计基于 ADB 语义化路由与重启持久化的自动化验收实践 导读 本文以 Pure Live com.mystyle.p音视频直播移动开发Pomotroid 窗口位置记忆基于 Tauri 2 的主窗口几何状态持久化与多显示器恢复方案Pomotroid 窗口位置记忆基于 Tauri 2 的主窗口几何状态持久化与多显示器恢复方案 Pomotroid 项目根目录 https://link.gPure Live v3.1.0 Android/Windows 验收矩阵历史增量全解读Issue 驱动的修复账本、事务化回归与发布门禁Pure Live v3.1.0 Android/Windows 验收矩阵历史增量全解读Issue 驱动的修复账本、事务化回归与发布门禁 本文以 docs/A音视频直播移动开发上一篇FalconFS监控与运维保障大规模AI集群存储稳定性的最佳实践下一篇Jailhouse-gui开发者手册核心组件与代码实现原理深度剖析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。