Flutter 系列 如何在Flutter中嵌入H5页面1. 介绍一下webview WebView 是一种可以在移动应
1. 介绍一下webview
WebView 是一种可以在移动应用或桌面应用中嵌入网页内容的组件。
一、功能特点
-
显示网页内容
- 它能够加载并显示 HTML、CSS 和 JavaScript 等网页技术构建的页面内容。你可以通过 WebView 来展示在线文档、新闻文章、网页表单等各种网络资源,就像在一个小型的内置浏览器中一样。
- 例如,在一些新闻类应用中,通过 WebView 加载新闻网站的页面,让用户可以直接在应用内阅读新闻,无需跳转到外部浏览器。
-
与原生应用交互
-
WebView 允许网页中的 JavaScript 代码与原生应用进行交互。这意味着网页可以调用原生应用的功能,原生应用也可以向网页传递数据或执行特定操作。
-
比如,一个电商应用中,网页端的购物车结算功能可能需要调用原生应用的支付接口来完成支付操作。
-
二、应用场景
-
混合开发
- 在移动应用开发中,WebView 常被用于混合开发模式。开发人员可以利用前端技术(如 HTML、CSS 和 JavaScript)开发部分功能界面,然后通过 WebView 嵌入到原生应用中,这样可以提高开发效率,同时降低开发成本。
- 例如,一些企业级应用可能会采用混合开发模式,以便快速迭代和更新部分功能模块。
-
内容展示
- 用于展示动态的、需要频繁更新的内容。由于网页内容可以随时在服务器端进行更新,而不需要更新整个应用,所以对于那些需要及时推送新信息的应用场景非常适用。
- 比如金融类应用展示实时的股票行情信息,或者社交应用中展示动态的广告内容。
2. flutter Webview 插件
flutter_webview
是 Flutter 中的插件,用于在应用中显示网页内容。它能加载指定 URL、支持 JavaScript 与 Dart 交互、提供导航控制等功能,具有跨平台、简洁易用、性能优化等优势,但使用时要注意安全、兼容性和性能调优问题。
3. 使用展示
3.1 安装插件
打开项目下的pubspec.yaml
文件, 在dependencies
下写入以下内容
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8
webview_flutter: ^4.10.0
3.2 代码编写
3.2.1 定义变量
- 定义变量,用于接收
class _MyHomePageState extends State<MyHomePage> {
WebViewController? _webViewController; // WebView 的控制器
}
3.2.2 实例化控制器,设置基本配置
- 定义初始化方法
class _MyHomePageState extends State<MyHomePage> {
WebViewController? _webViewController; // WebView 的控制器
@override
void initState() {
super.initState();
// 初始化 WebViewController
_initializeWebView();
}
// 初始化 WebView 的方法
Future<void> _initializeWebView() async {
// 确保 WebView 已经初始化
_webViewController = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted) // 允许 JavaScript
..setBackgroundColor(const Color(0x00000000)) // 设置透明背景
..loadRequest(Uri.parse('https://flutter.dev')); // 加载初始 URL
}
}
3.2.3 添加导航期间的回调函数
// 初始化 WebView 的方法
Future<void> _initializeWebView() async {
// 确保 WebView 已经初始化
_webViewController = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted) // 允许 JavaScript
..setBackgroundColor(const Color(0x00000000)) // 设置透明背景
..setNavigationDelegate(
NavigationDelegate(
onProgress: (progress) {
setState(() {
_progress = progress; // 更新加载进度
});
},
onPageStarted: (url) {
// 页面开始加载时调用
},
onPageFinished: (url) {
// 页面加载完成时调用
},
onWebResourceError: (error) {
// 处理网页资源错误(例如,显示错误消息)
},
onNavigationRequest: (request) {
// 阻止导航到 YouTube 链接
if (request.url.startsWith('https://www.youtube.com/')) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('提示'),
content: const Text('该站点(YouTube)被拦截,无法访问'),
actions: [
TextButton(
child: const Text('确定'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
});
return NavigationDecision.prevent;
}
return NavigationDecision.navigate; // 允许导航到其他 URL
},
),
)
..loadRequest(Uri.parse('https://flutter.dev')); // 加载初始 URL
}
我们通过onNavigationRequest回调函数, 捕获到request,通过判断request 的 url 是否以
https://www.youtube.com/
开头, 将所有www.youtube.com/ 开头的请求url 进行拦截.
4. 方法讲解
4.1 WebViewController类说明
WebViewController类
_webViewController = WebViewController() -> 控制主机平台提供的 WebView。
将其传递给WebViewWidget以显示 WebView。
一个WebViewController一次只能被一个WebViewWidget使用。
4.2 setJavaScriptMode方法说明
setJavaScriptMode方法 设置 WebView 使用的 JavaScript 执行模式。
- disabled 禁止的
- unrestricted 不受限制的
两种模式的说明 (借助ai打比方: 以下是对两种 JavaScript 模式的间接说明:
- 模式(unrestricted): 想象一下你拥有一把万能钥匙,可以打开任何一扇门,进入任何一个房间。无限制的 JavaScript 模式就如同这把万能钥匙。在这种模式下,你可以尽情地发挥 JavaScript 的强大威力,就像一位技艺高超的魔法师,能够施展各种复杂而神奇的魔法。你可以调用各种高级的 JavaScript 特性和第三方库,创造出令人惊叹的交互效果和功能强大的应用程序。然而,这把万能钥匙也并非没有风险。就像随意进入未知的房间可能会遇到危险一样,无限制模式可能会让恶意的代码有机可乘,给你的系统带来安全隐患。如果不小心使用,可能会引发意想不到的后果,就像魔法失控可能会带来灾难一样。
- 受限模式: 与之相反,受限模式更像是一个有守卫的城堡。城堡的大门只对特定的人开放,并且进入城堡后,你只能在特定的区域活动。在受限的 JavaScript 模式下,你的行动受到一定的限制。你不能随意使用某些可能存在风险的语法结构或功能,就像在城堡中不能随意进入某些禁地一样。这种限制虽然会在一定程度上降低你的灵活性,但它也为你的系统提供了更高的安全性。守卫会阻止恶意的代码进入城堡,保护你的数据和系统不受攻击。同时,受限模式也可以让你的代码更加规范和易于维护,就像城堡中的布局更加有序,便于管理一样。
- 总结 : 无限制模式下的 JavaScript 允许任意代码执行,具有极大灵活性但存在安全风险;受限模式对语法和功能进行限制,提高安全性但降低了灵活性。
4.3 loadRequest方法说明
方法定义, 其中url为必传参数, 剩下的method,headers,body为命名可选参数,可传可不传. 该方法会发送特定的Http 请求并在webview中加载响应
Future<void> loadRequest(
Uri uri, {
LoadRequestMethod method = LoadRequestMethod.get,
Map<String, String> headers = const <String, String>{},
Uint8List? body,
})
4.4 setNavigationDelegate方法
主要用来设置包含在导航事件期间可调用的一些回调方法的. 该方法传入了一个NavigationDelegate类型的参数
Future<void> setNavigationDelegate(NavigationDelegate delegate) {
return platform.setPlatformNavigationDelegate(delegate.platform);
}
NavigationDelegate的一些回调函数
- onProgress --> 在页面加载时调用以报告进度。
- onPageStarted --> 当页面开始加载时调用
- onPageFinished --> 页面加载完成时调用
- onWebResourceError -> 处理网页资源错误(例如,显示错误消息)
- onNavigationRequest --> 当导航请求的决策尚未完成时调用。(当 WebView 启动导航时(例如,当用户单击链接时),将调用此委托并决定如何继续导航。)
- prevent 阻止导航继续进行
- navigate 放行
5, 完整代码
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: 'Flutter Demo',
theme: ThemeData(
// 设置应用的颜色方案
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue.shade200),
useMaterial3: true, // 启用 Material 3
),
home: const MyHomePage(title: 'Flutter WebView Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
const MyHomePage({super.key, required this.title});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
WebViewController? _webViewController; // WebView 的控制器
int _progress = 0; // 加载进度
@override
void initState() {
super.initState();
// 初始化 WebViewController
_initializeWebView();
}
// 初始化 WebView 的方法
Future<void> _initializeWebView() async {
// 确保 WebView 已经初始化
_webViewController = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted) // 允许 JavaScript
..setBackgroundColor(const Color(0x00000000)) // 设置透明背景
..setNavigationDelegate(
NavigationDelegate(
onProgress: (progress) {
setState(() {
_progress = progress; // 更新加载进度
});
},
onPageStarted: (url) {
// 页面开始加载时调用
},
onPageFinished: (url) {
// 页面加载完成时调用
},
onWebResourceError: (error) {
// 处理网页资源错误(例如,显示错误消息)
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Icon(Icons.error),
content: Text('加载网页失败,错误信息:${error.description}'),
actions: [
TextButton(
child: const Text('确定'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
});
},
onNavigationRequest: (request) {
// 阻止导航到 YouTube 链接
if (request.url.startsWith('https://www.youtube.com/')) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('提示'),
content: const Text('该站点(YouTube)被拦截,无法访问'),
actions: [
TextButton(
child: const Text('确定'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
});
return NavigationDecision.prevent;
}
return NavigationDecision.navigate; // 允许导航到其他 URL
},
),
)
..loadRequest(Uri.parse('https://fl1utter.dev')); // 加载初始 URL
}
// 定义一个刷新页面的方法
void _reloadPage() {
if (_webViewController != null) {
_webViewController!.reload();
}
}
// 定义一个方法 跳转到
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Center(
child: Text(
widget.title,
style: const TextStyle(fontSize: 16),
),
),
actions: [
IconButton(
icon: const Icon(Icons.refresh),
onPressed: _reloadPage,
),
],
),
body: Center(
child: _webViewController == null
? const CircularProgressIndicator() // 显示加载指示器
: Stack(
children: [
WebViewWidget(controller: _webViewController!),
if (_progress < 100)
LinearProgressIndicator(
value: _progress / 100,
backgroundColor: Colors.white,
color: Colors.red,
),
],
),
),
);
}
}
6. 效果展示
转载自:https://juejin.cn/post/7424404476893642762