Flutter webview_flutter 4.x JS交互与高度自适应实战
详解 Flutter webview_flutter 4.x 版本的 JavaScript 双向交互、动态高度自适应以及 URL 拦截技巧,解决新版 API 变更带来的重构问题。
版本差异与核心变化
在开始编写代码前,需明确 webview_flutter 4.x 相比旧版本(3.x)的关键变化,避免使用过时的 API:
- 组件拆分:原有的 WebView 类被移除,拆分为
WebViewController(控制器/逻辑处理)和WebViewWidget(UI 渲染)。这种分离允许提前初始化 Controller。 - Android 适配简化:不再需要手动判断 Android 系统版本来选择 Hybrid Composition 还是 Texture Layer。插件内部已自动处理底层实现(Android 19+ 使用混合模式,23+ 使用纹理层)。
本文基于当前稳定版本 4.0.x 进行讲解,涵盖从环境准备到高级 JS 交互的完整流程。
环境准备
1. 配置最低 SDK 版本
虽然文档支持 Android SDK 19+,但为了保证插件兼容性及使用最新的 Android 特性(如 Texture Layer Hybrid Composition),建议将 minSdkVersion 设置为 21 或更高。
// android/app/build.gradle
android {
defaultConfig {
minSdkVersion 21
}
}
iOS 默认支持 11.0 及以上,无需额外调整。
2. 安装依赖
在项目根目录执行以下命令添加插件:
flutter pub add webview_flutter
基础示例与启用 JS
新建一个 Stateful Widget 来承载 WebView。关键在于初始化时启用非受限的 JavaScript 模式,否则大部分现代网页将无法运行。
class _MyWebViewState extends State<MyWebView> {
late final WebViewController controller;
double height = 0;
@override
void initState() {
super.initState();
// 1. 创建 Controller 并配置
controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted) // 必须开启 JS
..loadHtmlString('''
<!DOCTYPE html>
<html><body><h1>Hello World</h1></body></html>
''');
}
@override
Widget build(BuildContext context) {
return WebViewWidget(controller: controller);
}
}
WebView 高度自适应方案
WebViewWidget 会尝试占据最大可用空间。若未设置容器限制,会导致布局溢出;若使用固定高度的 SizedBox,内容过多时会遮挡或出现大片空白。理想的方案是根据页面实际内容高度动态调整。
方案一:网页主动报告高度
如果 H5 页面是团队内部可控的,最稳定的方式是让 JS 通过 postMessage 通知 Dart 侧页面高度。
H5 端 (HTML/JS):
<script>
const resizeObserver = new ResizeObserver(entries => {
Report.postMessage(document.scrollingElement.scrollHeight)
});
resizeObserver.observe(document.body);
</script>
Dart 端:
定义与 H5 中一致的 Channel 名称 Report,接收消息后更新状态。
controller.addJavaScriptChannel('Report', onMessageReceived: (message) {
setState(() {
height = double.parse(message.message);
});
});
方案二:无法修改源码时的注入方案
对于外部链接或第三方 H5,无法修改其源码时,需由 Flutter 侧注入 JS 脚本监听页面变化。
核心逻辑:
- 等待页面加载完成 (
onPageFinished)。 - 注入包含
ResizeObserver的脚本。 - 脚本将高度值发送给 Flutter 定义的 Channel。
controller.setNavigationDelegate(NavigationDelegate(
onPageFinished: (url) async {
// 页面加载完成后注入监听脚本
await controller.runJavaScript('''
const resizeObserver = new ResizeObserver(entries =>
Report.postMessage(document.scrollingElement.scrollHeight));
resizeObserver.observe(document.body);
''');
},
));
注意点:
- 必须在页面加载完成后注入,否则 DOM 尚未就绪,脚本无效。
- 可结合
Timer对频繁触发的消息进行节流,防止 UI 频繁重绘。 - 设置最大高度限制,防止无限增长导致内存或布局错误。
Dart 与 JS 双向交互
1. Dart 接收 JS 调用 (JavaScriptChannel)
定义一个名为 Jump 的通道,JS 端调用 Jump.postMessage('target') 即可触发 Flutter 回调。
controller.addJavaScriptChannel('Jump', onMessageReceived: (msg) {
if (msg.message == 'video') {
// 执行 Flutter 端的业务逻辑,如打开相机或播放视频
openVideoPlayer();
}
});
2. JS 调用 Dart 代码获取数据
Dart 可通过 runJavaScriptReturningResult 执行 JS 并返回值。
var result = await controller.runJavaScriptReturningResult('document.title');
3. 拦截 URL 跳转至原生页面
通过在 NavDelegate 中拦截特定 Scheme 的 URL,实现 H5 点击后打开 App 内原生页面。
controller.setNavigationDelegate(NavigationDelegate(
onNavigationRequest: (request) {
if (request.url.endsWith('/open-native-page')) {
return NavigationDecision.prevent; // 阻止 WebView 加载
}
return NavigationDecision.navigate;
},
));
H5 触发方式可以是 <a href="app://native"> 或 JS 修改 window.location.href。
最佳实践与总结
在进行 WebView 开发时,应遵循以下原则以权衡利弊:
- 控制权归属: 优先让 H5 页面自身处理布局和样式控制,Flutter 侧仅做必要的事件桥接。单方面强力注入 CSS/JS 虽灵活,但可能导致 H5 更新后的兼容性断裂。
- 性能优化: 避免在 JS 中频繁调用
postMessage,建议使用防抖或节流策略发送高度等高频状态。 - 加载方式: 示例中为了直观使用了
loadHtmlString,生产环境中建议使用loadRequest(Uri.parse(url))加载远程网址。
以上就是 js 交互在 Flutter 中使用 webview_flutter 的详细内容,更多关于 Flutter WebView 性能优化与跨端通信的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













