
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
🎯 欢迎来到 Flutter for OpenHarmony 社区!本文将深入讲解 Flutter 中 webview_flutter 内嵌浏览器组件的使用方法,带你全面掌握在应用中嵌入网页、JS交互、页面导航等功能。
一、webview_flutter 组件概述
在 Flutter for OpenHarmony 应用开发中,webview_flutter 是一个非常强大的内嵌浏览器组件,它允许开发者在应用中嵌入完整的网页浏览功能。通过 WebView,可以实现混合开发、加载 H5 页面、与 JavaScript 交互等功能,是连接原生应用与 Web 技术的桥梁。
📋 webview_flutter 组件特点
| 特点 | 说明 |
|---|---|
| 跨平台支持 | 支持 Android、iOS、Web、OpenHarmony |
| 完整浏览功能 | 支持页面导航、前进后退、刷新等 |
| JS 交互 | 支持 Flutter 与 JavaScript 双向通信 |
| 自定义控制 | 支持自定义 UserAgent、Cookie、Headers 等 |
| 回调监听 | 支持页面加载、导航、错误等事件监听 |
| 灵活配置 | 支持多种渲染模式(Hybrid Composition、Virtual Display) |
💡 使用场景:混合开发、H5页面嵌入、富文本展示、第三方登录、支付页面、协议页面等。
二、OpenHarmony 平台适配说明
2.1 兼容性信息
本项目基于 webview_flutter@4.13.0 开发,适配 Flutter 3.27.5-ohos-1.0.4。
2.2 支持的功能
在 OpenHarmony 平台上,webview_flutter 支持以下功能:
| 功能 | 说明 | OpenHarmony 支持 |
|---|---|---|
| 加载 URL | 加载网页链接 | ✅ yes |
| 加载 HTML | 加载本地 HTML 内容 | ✅ yes |
| 页面导航 | 前进、后退、刷新 | ✅ yes |
| JavaScript 交互 | JS 调用 Flutter | ✅ yes |
| Flutter 调用 JS | 执行 JavaScript 代码 | ✅ yes |
| Cookie 管理 | 设置和获取 Cookie | ✅ yes |
| UserAgent 设置 | 自定义 UserAgent | ✅ yes |
三、项目配置与安装
3.1 添加依赖配置
首先,需要在你的 Flutter 项目的 pubspec.yaml 文件中添加 webview_flutter 依赖。
打开项目根目录下的 pubspec.yaml 文件,找到 dependencies 部分,添加以下配置:
yaml
dependencies:
flutter:
sdk: flutter
# 添加 webview_flutter 依赖(OpenHarmony 适配版本)
webview_flutter:
git:
url: "https://atomgit.com/openharmony-tpc/flutter_packages.git"
path: "packages/webview_flutter/webview_flutter"
配置说明:
- 使用 git 方式引用开源鸿蒙适配的 flutter_packages 仓库
url:指定 AtomGit 托管的仓库地址path:指定 webview_flutter 包的具体路径ref:指定适配 OpenHarmony 的分支版本- 本项目基于
webview_flutter@4.13.0开发,适配 Flutter 3.27.5-ohos-1.0.4
⚠️ 重要:对于 OpenHarmony 平台,必须使用 git 方式引用适配版本,不能直接使用 pub.dev 的版本号。
3.2 下载依赖
配置完成后,需要在项目根目录执行以下命令下载依赖:
bash
flutter pub get
执行成功后,你会看到类似以下的输出:
Running "flutter pub get" in my_cross_platform_app...
Resolving dependencies...
Got dependencies!
运行 flutter pub get 时遇到 "File name too long"(文件名过长)问题。
打开 Git Bash 或 运行 cmd(需将 Git 添加到环境变量中),执行以下命令:
git config --global core.longpaths true
3.3 权限配置
WebView 需要网络权限,确保在 OpenHarmony 配置中添加了网络权限:
ohos/entry/src/main/module.json5:
json
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.INTERNET",
"reason": "$string:network_reason",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
}
]
}
3.4 添加权限原因说明
打开 ohos/entry/src/main/resources/base/element/string.json 文件,添加权限原因的字符串:
json
{
"string": [
{
"name": "network_reason",
"value": "使用网络"
}
]
}
四、webview_flutter 基础用法
4.1 导入库
在使用 webview_flutter 之前,需要先导入库:
dart
import 'package:webview_flutter/webview_flutter.dart';
4.2 创建基本 WebView
dart
class SimpleWebView extends StatefulWidget {
const SimpleWebView({super.key});
@override
State<SimpleWebView> createState() => _SimpleWebViewState();
}
class _SimpleWebViewState extends State<SimpleWebView> {
late final WebViewController _controller;
@override
void initState() {
super.initState();
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..loadRequest(Uri.parse('https://www.baidu.com'));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('WebView')),
body: WebViewWidget(controller: _controller),
);
}
}
4.3 加载 URL
dart
// 加载网络 URL
_controller.loadRequest(Uri.parse('https://www.example.com'));
// 加载带参数的 URL
_controller.loadRequest(
Uri.parse('https://www.example.com/search').replace(
queryParameters: {'q': 'flutter'},
),
);
4.4 加载 HTML 内容
dart
// 加载 HTML 字符串
_controller.loadHtmlString('''
<html>
<head><title>本地页面</title></head>
<body>
<h1>Hello WebView</h1>
<p>这是一个本地 HTML 内容</p>
</body>
</html>
''');
// 加载本地 HTML 文件
_controller.loadFlutterAsset('assets/index.html');
4.5 页面导航控制
dart
// 后退
await _controller.goBack();
// 前进
await _controller.goForward();
// 刷新
await _controller.reload();
// 停止加载
await _controller.goBack();
五、常用 API 详解
5.1 WebViewController - 控制器
WebViewController 是 WebView 的核心控制器,提供所有控制方法:
dart
final WebViewController controller = WebViewController();
5.2 主要配置方法
dart
WebViewController controller = WebViewController()
// 启用 JavaScript
..setJavaScriptMode(JavaScriptMode.unrestricted)
// 设置导航代理
..setNavigationDelegate(NavigationDelegate(
onProgress: (int progress) {
// 页面加载进度
},
onPageStarted: (String url) {
// 页面开始加载
},
onPageFinished: (String url) {
// 页面加载完成
},
onWebResourceError: (WebResourceError error) {
// 加载错误
},
onNavigationRequest: (NavigationRequest request) {
// 导航请求拦截
return NavigationDecision.navigate;
},
))
// 设置 UserAgent
..setUserAgent('MyApp/1.0')
// 启用缩放
..enableZoom(true);
5.3 JavaScript 交互
Flutter 调用 JavaScript:
dart
// 执行 JavaScript 并获取返回值
final result = await _controller.runJavaScript('document.title');
print('页面标题: $result');
// 执行无返回值的 JavaScript
_controller.runJavaScript('alert("Hello from Flutter")');
JavaScript 调用 Flutter:
dart
// 注册 JavaScript 通道
_controller.addJavaScriptChannel(
'FlutterChannel',
onMessageReceived: (JavaScriptMessage message) {
print('收到 JS 消息: ${message.message}');
},
);
// JavaScript 端调用
// FlutterChannel.postMessage('Hello from JavaScript');
5.4 Cookie 管理
dart
// 获取 Cookie
final cookies = await _controller.runJavaScript('document.cookie');
// 设置 Cookie
await _controller.runJavaScript('document.cookie = "name=value"');
六、实际应用场景
6.1 带进度条的 WebView
dart
class ProgressWebView extends StatefulWidget {
final String url;
final String title;
const ProgressWebView({
super.key,
required this.url,
required this.title,
});
@override
State<ProgressWebView> createState() => _ProgressWebViewState();
}
class _ProgressWebViewState extends State<ProgressWebView> {
late final WebViewController _controller;
int _loadingProgress = 0;
bool _isLoading = false;
@override
void initState() {
super.initState();
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(NavigationDelegate(
onProgress: (int progress) {
setState(() {
_loadingProgress = progress;
});
},
onPageStarted: (String url) {
setState(() {
_isLoading = true;
});
},
onPageFinished: (String url) {
setState(() {
_isLoading = false;
});
},
))
..loadRequest(Uri.parse(widget.url));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
actions: [
IconButton(
icon: const Icon(Icons.refresh),
onPressed: () => _controller.reload(),
),
],
),
body: Column(
children: [
if (_isLoading)
LinearProgressIndicator(
value: _loadingProgress / 100.0,
backgroundColor: Colors.grey[200],
valueColor: const AlwaysStoppedAnimation<Color>(Colors.blue),
),
Expanded(
child: WebViewWidget(controller: _controller),
),
],
),
);
}
}
6.2 带导航栏的 WebView
dart
class NavigationWebView extends StatefulWidget {
final String initialUrl;
const NavigationWebView({super.key, required this.initialUrl});
@override
State<NavigationWebView> createState() => _NavigationWebViewState();
}
class _NavigationWebViewState extends State<NavigationWebView> {
late final WebViewController _controller;
bool _canGoBack = false;
bool _canGoForward = false;
String _currentUrl = '';
@override
void initState() {
super.initState();
_currentUrl = widget.initialUrl;
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(NavigationDelegate(
onPageFinished: (String url) async {
_currentUrl = url;
_updateNavigationState();
},
))
..loadRequest(Uri.parse(widget.initialUrl));
}
Future<void> _updateNavigationState() async {
final canGoBack = await _controller.canGoBack();
final canGoForward = await _controller.canGoForward();
setState(() {
_canGoBack = canGoBack;
_canGoForward = canGoForward;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('WebView')),
body: Column(
children: [
Expanded(
child: WebViewWidget(controller: _controller),
),
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.grey[100],
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 4,
),
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: _canGoBack ? () => _controller.goBack() : null,
),
IconButton(
icon: const Icon(Icons.arrow_forward),
onPressed: _canGoForward ? () => _controller.goForward() : null,
),
IconButton(
icon: const Icon(Icons.refresh),
onPressed: () => _controller.reload(),
),
IconButton(
icon: const Icon(Icons.home),
onPressed: () => _controller.loadRequest(Uri.parse(widget.initialUrl)),
),
],
),
),
],
),
);
}
}
6.3 JavaScript 双向通信
dart
class JsInteractionWebView extends StatefulWidget {
const JsInteractionWebView({super.key});
@override
State<JsInteractionWebView> createState() => _JsInteractionWebViewState();
}
class _JsInteractionWebViewState extends State<JsInteractionWebView> {
late final WebViewController _controller;
final TextEditingController _messageController = TextEditingController();
String _receivedMessage = '';
@override
void initState() {
super.initState();
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..addJavaScriptChannel(
'FlutterChannel',
onMessageReceived: (JavaScriptMessage message) {
setState(() {
_receivedMessage = message.message;
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('收到 JS 消息: ${message.message}')),
);
},
)
..loadHtmlString('''
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body { font-family: Arial; padding: 20px; }
button { padding: 10px 20px; margin: 10px; font-size: 16px; }
input { padding: 10px; font-size: 16px; width: 80%; }
</style>
</head>
<body>
<h1>Flutter 与 JS 交互</h1>
<input type="text" id="messageInput" placeholder="输入消息">
<br>
<button onclick="sendToFlutter()">发送给 Flutter</button>
<div id="result" style="margin-top: 20px; padding: 10px; background: #f0f0f0;"></div>
<script>
function sendToFlutter() {
var message = document.getElementById('messageInput').value;
FlutterChannel.postMessage(message);
}
function receiveFromFlutter(message) {
document.getElementById('result').innerText = 'Flutter 说: ' + message;
}
</script>
</body>
</html>
''');
}
void _sendToJs() {
final message = _messageController.text;
_controller.runJavaScript("receiveFromFlutter('$message')");
_messageController.clear();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('JS 交互')),
body: Column(
children: [
Expanded(
child: WebViewWidget(controller: _controller),
),
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.grey[100],
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 4,
offset: const Offset(0, -2),
),
],
),
child: Column(
children: [
if (_receivedMessage.isNotEmpty)
Text('最后收到: $_receivedMessage'),
const SizedBox(height: 8),
Row(
children: [
Expanded(
child: TextField(
controller: _messageController,
decoration: const InputDecoration(
hintText: '输入消息发送给 JS',
border: OutlineInputBorder(),
),
),
),
const SizedBox(width: 8),
ElevatedButton(
onPressed: _sendToJs,
child: const Text('发送'),
),
],
),
],
),
),
],
),
);
}
}
6.4 URL 拦截与处理
dart
class InterceptWebView extends StatefulWidget {
const InterceptWebView({super.key});
@override
State<InterceptWebView> createState() => _InterceptWebViewState();
}
class _InterceptWebViewState extends State<InterceptWebView> {
late final WebViewController _controller;
@override
void initState() {
super.initState();
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(NavigationDelegate(
onNavigationRequest: (NavigationRequest request) {
// 拦截特定 URL
if (request.url.contains('tel:')) {
// 处理电话链接
_handleTelLink(request.url);
return NavigationDecision.prevent;
}
if (request.url.contains('mailto:')) {
// 处理邮件链接
_handleMailLink(request.url);
return NavigationDecision.prevent;
}
if (request.url.contains('payment')) {
// 拦截支付页面,跳转到原生支付
_handlePayment(request.url);
return NavigationDecision.prevent;
}
return NavigationDecision.navigate;
},
))
..loadRequest(Uri.parse('https://www.example.com'));
}
void _handleTelLink(String url) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('拨打电话: $url')),
);
}
void _handleMailLink(String url) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('发送邮件: $url')),
);
}
void _handlePayment(String url) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('跳转到原生支付页面')),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('URL 拦截')),
body: WebViewWidget(controller: _controller),
);
}
}
6.5 自定义 UserAgent 和 Headers
dart
class CustomWebView extends StatefulWidget {
const CustomWebView({super.key});
@override
State<CustomWebView> createState() => _CustomWebViewState();
}
class _CustomWebViewState extends State<CustomWebView> {
late final WebViewController _controller;
@override
void initState() {
super.initState();
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setUserAgent('MyApp/1.0 (Mobile; OpenHarmony)')
..loadRequest(
LoadRequestParams(
uri: Uri.parse('https://www.example.com'),
headers: {
'Authorization': 'Bearer token123',
'X-Custom-Header': 'CustomValue',
},
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('自定义配置')),
body: WebViewWidget(controller: _controller),
);
}
}
七、完整示例代码
下面是一个完整的示例应用,展示了 webview_flutter 的各种用法:
dart
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'WebView 示例',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF6366F1)),
useMaterial3: true,
),
home: const WebViewDemoPage(),
);
}
}
class WebViewDemoPage extends StatefulWidget {
const WebViewDemoPage({super.key});
@override
State<WebViewDemoPage> createState() => _WebViewDemoPageState();
}
class _WebViewDemoPageState extends State<WebViewDemoPage> {
late final WebViewController _controller;
int _loadingProgress = 0;
bool _isLoading = false;
bool _canGoBack = false;
bool _canGoForward = false;
String _currentTitle = '';
@override
void initState() {
super.initState();
_initController();
}
void _initController() {
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(NavigationDelegate(
onProgress: (int progress) {
setState(() {
_loadingProgress = progress;
});
},
onPageStarted: (String url) {
setState(() {
_isLoading = true;
});
},
onPageFinished: (String url) async {
setState(() {
_isLoading = false;
});
_updateNavigationState();
final title = await _controller.getTitle();
if (title != null) {
setState(() {
_currentTitle = title;
});
}
},
onWebResourceError: (WebResourceError error) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('加载失败: ${error.description}')),
);
},
))
..addJavaScriptChannel(
'FlutterChannel',
onMessageReceived: (JavaScriptMessage message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('收到消息: ${message.message}')),
);
},
)
..loadRequest(Uri.parse('https://www.baidu.com'));
}
Future<void> _updateNavigationState() async {
final canGoBack = await _controller.canGoBack();
final canGoForward = await _controller.canGoForward();
setState(() {
_canGoBack = canGoBack;
_canGoForward = canGoForward;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_currentTitle.isEmpty ? 'WebView 示例' : _currentTitle),
centerTitle: true,
elevation: 0,
actions: [
PopupMenuButton<String>(
onSelected: _handleMenuSelection,
itemBuilder: (context) => [
const PopupMenuItem(value: 'refresh', child: Text('刷新')),
const PopupMenuItem(value: 'baidu', child: Text('百度')),
const PopupMenuItem(value: 'bing', child: Text('必应')),
const PopupMenuItem(value: 'github', child: Text('GitHub')),
const PopupMenuItem(value: 'js_test', child: Text('JS交互测试')),
],
),
],
),
body: Column(
children: [
if (_isLoading)
LinearProgressIndicator(
value: _loadingProgress / 100.0,
backgroundColor: Colors.grey[200],
valueColor: const AlwaysStoppedAnimation<Color>(Color(0xFF6366F1)),
),
Expanded(
child: WebViewWidget(controller: _controller),
),
_buildBottomNavigationBar(),
],
),
);
}
Widget _buildBottomNavigationBar() {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 4,
offset: const Offset(0, -2),
),
],
),
child: SafeArea(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: _canGoBack ? () => _controller.goBack() : null,
color: _canGoBack ? const Color(0xFF6366F1) : Colors.grey,
),
IconButton(
icon: const Icon(Icons.arrow_forward),
onPressed: _canGoForward ? () => _controller.goForward() : null,
color: _canGoForward ? const Color(0xFF6366F1) : Colors.grey,
),
IconButton(
icon: const Icon(Icons.refresh),
onPressed: () => _controller.reload(),
color: const Color(0xFF6366F1),
),
IconButton(
icon: const Icon(Icons.home),
onPressed: () => _controller.loadRequest(Uri.parse('https://www.baidu.com')),
color: const Color(0xFF6366F1),
),
],
),
),
);
}
void _handleMenuSelection(String value) {
switch (value) {
case 'refresh':
_controller.reload();
break;
case 'baidu':
_controller.loadRequest(Uri.parse('https://www.baidu.com'));
break;
case 'bing':
_controller.loadRequest(Uri.parse('https://www.bing.com'));
break;
case 'github':
_controller.loadRequest(Uri.parse('https://github.com'));
break;
case 'js_test':
_loadJsTestPage();
break;
}
}
void _loadJsTestPage() {
_controller.loadHtmlString('''
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
margin: 0;
}
.container {
background: white;
border-radius: 16px;
padding: 24px;
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
}
h1 { color: #333; margin-top: 0; }
input {
width: 100%;
padding: 12px;
border: 2px solid #e0e0e0;
border-radius: 8px;
font-size: 16px;
box-sizing: border-box;
margin-bottom: 16px;
}
input:focus {
border-color: #6366F1;
outline: none;
}
button {
width: 100%;
padding: 14px;
background: linear-gradient(135deg, #6366F1 0%, #8B5CF6 100%);
color: white;
border: none;
border-radius: 8px;
font-size: 16px;
font-weight: bold;
cursor: pointer;
}
button:active {
transform: scale(0.98);
}
#result {
margin-top: 20px;
padding: 16px;
background: #f5f5f5;
border-radius: 8px;
min-height: 50px;
}
</style>
</head>
<body>
<div class="container">
<h1>🚀 Flutter 与 JS 交互测试</h1>
<input type="text" id="messageInput" placeholder="输入消息发送给 Flutter">
<button onclick="sendToFlutter()">发送给 Flutter</button>
<div id="result">等待消息...</div>
</div>
<script>
function sendToFlutter() {
var message = document.getElementById('messageInput').value || 'Hello!';
FlutterChannel.postMessage(message);
document.getElementById('result').innerText = '已发送: ' + message;
}
function receiveFromFlutter(message) {
document.getElementById('result').innerText = 'Flutter 说: ' + message;
}
</script>
</body>
</html>
''');
}
}
八、常见问题与解决方案
8.1 页面空白
问题原因:
- 未启用 JavaScript
- 网络权限未配置
解决方案:
dart
_controller.setJavaScriptMode(JavaScriptMode.unrestricted);
8.2 HTTPS 混合内容无法加载
问题原因:
- HTTPS 页面加载 HTTP 资源被阻止
解决方案:
在 OpenHarmony 配置中允许混合内容。
8.3 JavaScript 交互不生效
问题原因:
- 未启用 JavaScript 模式
- JavaScript 通道名称错误
解决方案:
dart
_controller
..setJavaScriptMode(JavaScriptMode.unrestricted)
..addJavaScriptChannel('FlutterChannel', onMessageReceived: (message) {
// 处理消息
});
8.4 返回键无法后退
问题原因:
- 未处理物理返回键
解决方案:
dart
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
if (await _controller.canGoBack()) {
_controller.goBack();
return false;
}
return true;
},
child: Scaffold(
body: WebViewWidget(controller: _controller),
),
);
}
九、总结
webview_flutter 是 Flutter for OpenHarmony 应用开发中实现混合开发的重要组件。通过本文的学习,我们掌握了:
- 基础用法:加载 URL、HTML 内容、页面导航
- JavaScript 交互:Flutter 与 JS 双向通信
- 事件监听:页面加载、导航、错误等事件
- URL 拦截:自定义处理特定链接
- 自定义配置:UserAgent、Headers、Cookie 等
💡 开发建议:使用 webview_flutter 时应注意:
- 始终启用 JavaScript 模式
- 处理好物理返回键
- 注意内存管理,及时释放资源
- 对于复杂交互,使用 JavaScript 通道通信