前几天 #175099 又报了一个 iOS 18.2 上的问题:webview_flutter 里的点击偶尔失灵,点不动或者点了没反应。根子还是 WKWebView 内部的手势识别器和 Flutter Engine 里那个用来阻止/延迟手势的 recognizer 产生了冲突。
去年 iOS 18.2 beta 就闹过一回。当时 Engine 那边通过 #56804 搞了一个临时绕过——把 delayingRecognizer 先移除再加回来,利用这个操作刷新 WebKit 的内部状态,让点击恢复。但这个补丁在 iOS 18.2 正式版上造成了更麻烦的回归:overlay 的手势阻止会失效,触摸直接穿透到下面的 WebView。所以 Flutter 团队在 iOS 18.2 条件下又把那个提交回退了。

Flutter 那边也确认了这是 Apple / WebKit 自己的 bug,已经同步上报,两边在协作修复。
到底哪里不对
最早是在 iOS 18.2 beta 上发现的:页面上先触发过某个 Flutter widget 或 overlay(比如 context menu、Drawer),然后 WKWebView 里的链接、按钮就点不动了——能看到高亮,但不会跳转。重新加载 WebView 能恢复。
原因得从 Flutter 在 iOS 上展示 PlatformView 的那套手势机制说起。Flutter 会在承载 WKWebView 的视图上加一个 FlutterDelayingGestureRecognizer(delayingRecognizer),通过切换它的状态(possible、ended、failed)来告诉 UIKit 的其他 recognizer 该不该拦手势。

稍微啰嗦一句背景:Flutter 和 iOS 各自有一套手势识别系统。把一个原生控件(比如 WKWebView)嵌进 Flutter 时,层级大概是这样的:
[FlutterView] ← 整个 Flutter 渲染层
├─ Flutter widgets
│ ↑
│ │
│ 手势由 Flutter framework(Dart)处理
│ └─ PlatformView (e.g. WKWebView)
│ ↑
│ 手势由 UIKit / WebKit 内部 recognizer 处理
为了防止互相抢事件,Flutter Engine 在 iOS 上引入了一个'延迟识别器'(delaying gesture recognizer)。简单说就是:当 Flutter 检测到某个 widget 要阻止事件(比如 GestureDetector 或者 overlay 遮罩),它就通过这个 delayingRecognizer 让 UIKit 里的 recognizer(包括 WKWebView 的点击识别器)暂停响应。
这套机制在 Flutter 和 UIKit 手势交界处非常敏感。这次的 bug 就是:WKWebView 内部的某些 recognizer 会'缓存'或持有 的旧状态。Flutter 在运行时切换 状态(比如 )时,WebKit 的部分识别器拿到的是过期状态,结果就是只高亮不执行动作。








