近日,多位 Flutter 开发者在使用 flutter_inappwebview 插件集成 Web 页面时,发现一个困扰已久的 iOS 端兼容问题:当软件键盘弹出时,HTML 页面中的固定头部(fixed header)出现明显的上下移动或抖动现象。该问题在设置 resizeToAvoidBottomInset 属性为 true 或 false 时表现截然不同,引发社区广泛讨论。
问题现象:固定头部“跳起舞”
在典型的混合开发场景中,开发者常利用 InAppWebView 加载包含固定头部的 H5 页面。固定头部通常通过 CSS position: fixed; top: 0 实现,确保用户在滚动内容时导航栏始终可见。然而,在 iOS 设备上(尤其是 iPhone 全面屏机型),一旦点击输入框触发键盘,固定头部会先向上弹跳一段距离,随后又回落,或出现持续微幅抖动,严重影响用户体验。
有开发者录制了对比视频:在 Safari 原生浏览器中,同一页面行为正常,而嵌入 flutter_inappwebview 后问题复现。这表明问题根源在于 Flutter WebView 与 iOS 键盘交互的机制。
根源剖析:resizeToAvoidBottomInset 的“双面性”
Flutter 的 Scaffold 组件提供了 resizeToAvoidBottomInset 属性(默认 true),其作用是当键盘弹出时,自动调整底部内边距,以避免输入框被遮挡。这一机制在原生 Flutter Widget 中工作良好,但在嵌入 WebView 时却引发连锁反应。
当 resizeToAvoidBottomInset = true(默认值)时,Flutter 会缩小 Scaffold 的可用高度,导致内嵌的 InAppWebView 高度被压缩。此时 iOS WebKit 会重新计算页面布局,触发 window.resize 事件。由于 position: fixed 元素在 iOS 上的渲染依赖于视口高度而非文档高度,布局重算会让 WebView 误以为视口缩小,从而“纠正”固定元素的顶部位置,造成视觉上的跳动。更糟的是,键盘弹出和收回过程的异步时序可能导致多次 resize,引发持续抖动。
而当 resizeToAvoidBottomInset = false 时,Flutter 不会调整底部内边距,WebView 高度保持不变。然而,这又带来另一个问题:键盘仍会弹出并覆盖 WebView 底部,输入框可能被完全遮挡,用户无法看到正在输入的内容。开发者陷入“保头部正常”还是“保输入框可见”的两难。
社区方案:折中与 hack
在 GitHub 相关 issue 中,开发者提出了多种临时解决方案:
- 手动监听键盘高度:通过
MediaQuery.of(context).viewInsets.bottom获取键盘高度,动态调整 WebView 的padding或margin,同时将resizeToAvoidBottomInset设为false,由开发者自行控制布局。 - CSS 层 Hack:在 H5 页面中利用
@supports (top: env(safe-area-inset-top))或监听visualViewportAPI 来调整 fixed 元素位置,但兼容性有限。 - 原生插件修改:部分开发者 Fork 了
flutter_inappwebview,在 iOS 端的 WKWebView 配置中设置_webView.scrollView.contentInsetAdjustmentBehavior = .never,并配合resizeToAvoidBottomInset = false实现较为平滑的效果。
截至发稿,flutter_inappwebview 官方尚未发布正式修复。插件主要维护者已在 Roadmap 中标记为“高优先级”,但未提供时间表。
对开发者的启示
该问题暴露了 Flutter 混合开发中一个深层矛盾:原生控件(键盘)与 WebView 布局之间的信息同步存在延迟与歧义。对于需要同时保证固定头部稳定性和输入框可见性的项目,建议:
- 在 iOS 端默认设置
resizeToAvoidBottomInset = false,避免 WebView 高度被频繁改变。 - 采用
flutter_keyboard_visibility等第三方插件监听键盘状态,手动调整 WebView 的bottompadding。 - 评估是否可将固定头部迁移至 Flutter 层实现,仅将内容区域作为 WebView,彻底隔离布局冲突。
随着 Flutter 在电商、社交等重交互场景中的普及,此类混合渲染的细节问题将愈发考验开发者的功底。在官方修复到来之前,理解 resizeToAvoidBottomInset 的底层逻辑,或许就是破解“抖动魔咒”的第一把钥匙。