Flutter for OpenHarmony 实战:webview_flutter — 混合开发核心
摘要: 本文介绍了在OpenHarmony平台上使用Flutter的webview_flutter插件实现混合开发的方法。该插件基于OpenHarmony原生ArkWeb内核,支持网页加载、JS交互和导航控制。文章详细讲解了核心组件WebViewController和WebViewWidget的作用,提供了安装配置步骤,包括依赖添加和权限申请。通过代码示例演示了网页加载、进度监听等基础功能,以及F
Flutter for OpenHarmony 实战:webview_flutter — 混合开发核心
前言
在现代 App 开发中,混合开发(Hybrid Development)是一种非常流行的模式。通过将部分业务逻辑或展示页面使用 Web 技术(HTML/CSS/JS)实现,可以极大地提高开发效率和动态更新能力。每个成熟的 App 几乎都离不开 WebView。
在 Flutter 生态中,webview_flutter 是官方维护的首选插件。好消息是,该插件已经适配了 OpenHarmony 平台,基于系统原生的 ArkWeb 内核封装,性能优异且功能完备。
本文将带领大家在 OpenHarmony 项目中集成 webview_flutter,实现网页加载、JS 交互以及简单的导航控制。
一、核心组件与架构
webview_flutter 采用了“Platform View”的机制,将原生的 OpenHarmony WebView 组件嵌入到 Flutter 的 Widget 树中。
1.1 WebViewController
这是控制 WebView 的核心类。从 4.0 版本开始,API 发生了重大重构,不再通过 Widget 参数初始化,而是通过创建一个 WebViewController 实例来配置和管理 WebView。
1.2 WebViewWidget
这是一个纯展示用的 Widget,它接受一个 WebViewController 实例,负责在界面上渲染 WebView。
二、安装与配置
2.1 添加依赖
在 pubspec.yaml 中添加:
dependencies:
flutter:
sdk: flutter
# 请务必检查 pub.dev 或 OpenHarmony 社区以获取适配 OHOS 的最新版本
webview_flutter: ^4.7.0
dependency_overrides:
# WebView 全套覆盖,防止 Interface 版本冲突
webview_flutter:
git:
url: "https://atomgit.com/openharmony-tpc/flutter_packages.git"
path: "packages/webview_flutter/webview_flutter"
webview_flutter_platform_interface:
git:
url: "https://atomgit.com/openharmony-tpc/flutter_packages.git"
path: "packages/webview_flutter/webview_flutter_platform_interface"
webview_flutter_ohos:
git:
url: "https://atomgit.com/openharmony-tpc/flutter_packages.git"
path: "packages/webview_flutter/webview_flutter_ohos"
2.2 OpenHarmony 权限配置(必做)
WebView 加载网页属于网络操作,必须申请网络权限。打开 entry/src/main/module.json5:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
}
}
此外,OpenHarmony 默认禁止明文流量(HTTP)。如果需要加载 HTTP 链接,需在 module.json5 中配置 requestPermissions 同级的 metadata 或查阅最新的网络安全配置指南(通常建议全站 HTTPS)。

三、基础用法:加载网页
3.1 初始化控制器
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class WebViewPage extends StatefulWidget {
const WebViewPage({super.key});
State<WebViewPage> createState() => _WebViewPageState();
}
class _WebViewPageState extends State<WebViewPage> {
late final WebViewController _controller;
void initState() {
super.initState();
// 1. 创建控制器
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted) // 允许执行 JS
..setBackgroundColor(const Color(0x00000000)) // 设置背景透明
..setNavigationDelegate(
NavigationDelegate(
onProgress: (int progress) {
debugPrint('加载进度: $progress%');
},
onPageStarted: (String url) {
debugPrint('开始加载: $url');
},
onPageFinished: (String url) {
debugPrint('加载完成: $url');
},
onWebResourceError: (WebResourceError error) {
debugPrint('加载错误: ${error.description}');
},
),
)
..loadRequest(Uri.parse('https://openharmony.cn')); // 加载目标 URL
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('OpenHarmony 官网')),
body: WebViewWidget(controller: _controller), // 2. 显示 WebView
);
}
}

四、进阶用法:JS 交互
WebView 的强大之处在于原生代码与网页脚本的交互。
4.1 Flutter 调用 JS
使用 runJavaScript 或 runJavaScriptReturningResult:
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class JsInteractionWebViewPage extends StatefulWidget {
const JsInteractionWebViewPage({super.key});
State<JsInteractionWebViewPage> createState() =>
_JsInteractionWebViewPageState();
}
class _JsInteractionWebViewPageState extends State<JsInteractionWebViewPage> {
late final WebViewController _controller;
String _currentTitle = '等待获取...';
void initState() {
super.initState();
// 构造一个简单的本地 HTML 用于演示 JS 交互
// 包含 4.2 章节中提到的 button 代码
const String htmlContent = '''
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>原始标题</title>
<style>
body { font-family: sans-serif; padding: 20px; text-align: center; }
button { font-size: 18px; padding: 10px 20px; margin-top: 20px; background-color: #007AFF; color: white; border: none; border-radius: 5px; }
</style>
</head>
<body>
<h1>JS 交互演示</h1>
<p id="msg">点击下方按钮调用 Flutter</p>
<!-- 4.2 章节示例代码 -->
<button onclick="Toaster.postMessage('我是来自 H5 的问候');">点击我调用 Flutter</button>
<script>
function changeColor() {
document.body.style.backgroundColor = document.body.style.backgroundColor === 'lightblue' ? 'white' : 'lightblue';
}
</script>
</body>
</html>
''';
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setBackgroundColor(const Color(0x00000000))
..setNavigationDelegate(
NavigationDelegate(
onPageFinished: (url) {
debugPrint('页面加载完毕');
},
),
)
// 4.2 配置 Channel
..addJavaScriptChannel(
'Toaster', // Channel 名称,JS 端通过 window.Toaster 访问
onMessageReceived: (JavaScriptMessage message) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('H5 传来的消息: ${message.message}'),
action: SnackBarAction(label: '确定', onPressed: () {}),
),
);
},
)
..loadHtmlString(htmlContent); // 加载本地 HTML
}
// 4.1 Flutter 调用 JS
Future<void> _changeWebTitle() async {
await _controller.runJavaScript("document.title = 'Hello from Flutter!';");
// 更新页面上的文字提示,以便直观看到效果
await _controller.runJavaScript(
"document.getElementById('msg').innerText = 'Flutter 修改了文档标题!';");
}
// 4.1 获取 JS 执行结果
Future<void> _getWebTitle() async {
// runJavaScriptReturningResult 返回的是 Object,通常是 String 或 num
final result =
await _controller.runJavaScriptReturningResult("document.title");
setState(() {
// 注意:返回的字符串可能包含引号,如 '"Hello from Flutter!"'
_currentTitle = result.toString();
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('JS 交互深度演示 (章节4)')),
body: Column(
children: [
// 操作控制区
Container(
padding: const EdgeInsets.all(16),
color: Colors.grey[100],
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: _changeWebTitle,
child: const Text('修改网页标题'),
),
ElevatedButton(
onPressed: _getWebTitle,
child: const Text('获取网页标题'),
),
],
),
const SizedBox(height: 12),
Text('当前获取到的标题: $_currentTitle',
style: const TextStyle(
fontWeight: FontWeight.bold, color: Colors.blue)),
],
),
),
const Divider(height: 1),
// WebView 展示区
Expanded(child: WebViewWidget(controller: _controller)),
],
),
);
}
}

五、OpenHarmony 平台适配细节
5.1 软键盘遮挡问题
在混合开发中,输入框被键盘遮挡是常见痛点。OpenHarmony 版本的 webview_flutter 通常已处理了 Resize 逻辑。建议在 Scaffold 中设置 resizeToAvoidBottomInset: true(默认即为 true)。
5.2 视频全屏播放
如果网页包含 <video> 标签,全屏播放可能需要额外的配置。请关注相关 Issue 或确保正确处理了 NavigationDelegate 中的请求。
5.3 性能调优
- 复用机制:WebView 是重资源组件,尽量避免频繁创建和销毁。
- 缓存:合理利用
clearCache和clearLocalStorage管理存储空间。
六、完整示例代码
本示例演示了一个带有加载进度条和 JS 交互功能的简易浏览器。
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class FullFeatureWebView extends StatefulWidget {
const FullFeatureWebView({super.key});
State<FullFeatureWebView> createState() => _FullFeatureWebViewState();
}
class _FullFeatureWebViewState extends State<FullFeatureWebView> {
late final WebViewController _controller;
double _progress = 0.0;
bool _initialized = false;
void initState() {
super.initState();
_initController();
}
void _initController() {
// 实例化 Controller
// 在实际开发中,可能需要根据平台 (Platform) 做不同的配置
// 但 webview_flutter 4.0+ 已经做了很好的抽象
final WebViewController controller = WebViewController();
controller
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setBackgroundColor(const Color(0x00000000))
..setNavigationDelegate(
NavigationDelegate(
onProgress: (int progress) {
if (!mounted) return;
setState(() {
_progress = progress / 100;
});
},
onPageStarted: (String url) {
debugPrint('开始加载: $url');
},
onPageFinished: (String url) {
debugPrint('加载完成: $url');
},
onWebResourceError: (WebResourceError error) {
debugPrint('加载错误: ${error.description}');
},
onNavigationRequest: (NavigationRequest request) {
if (request.url.startsWith('https://www.youtube.com/')) {
debugPrint('拦截了跳转: ${request.url}');
return NavigationDecision.prevent;
}
return NavigationDecision.navigate;
},
),
)
..addJavaScriptChannel(
'OnHarmony',
onMessageReceived: (message) {
if (!mounted) return;
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('JavaScript 消息'),
content: Text(message.message),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx),
child: const Text('关闭'),
)
],
),
);
},
)
..loadRequest(Uri.parse('https://flutter.dev'));
_controller = controller;
_initialized = true;
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('OHOS WebView'),
actions: [
IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () async {
if (_initialized && await _controller.canGoBack()) {
await _controller.goBack();
}
},
),
IconButton(
icon: const Icon(Icons.refresh),
onPressed: () {
if (_initialized) _controller.reload();
},
),
],
),
body: Column(
children: [
if (_progress < 1.0)
LinearProgressIndicator(value: _progress, color: Colors.blue),
Expanded(
child: _initialized
? WebViewWidget(controller: _controller)
: const Center(child: CircularProgressIndicator()),
),
],
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.code),
onPressed: () {
if (_initialized) {
_controller.runJavaScript(
"if(window.OnHarmony) { OnHarmony.postMessage('当前的 URL 是: ' + window.location.href) } else { alert('Channel 未就绪'); }");
}
},
),
);
}
}

七、总结
webview_flutter 在 OpenHarmony 上的表现已经相当成熟,足以支撑大部分混合开发场景。通过掌握 WebViewController 的配置和 JavaScriptChannel 的使用,开发者可以轻松构建功能丰富的 Web 容器应用。
🌐 欢迎加入开源鸿蒙跨平台社区:开源鸿蒙跨平台开发者社区
更多推荐



所有评论(0)