注入 JavaScript 到 Flutter Web View 中
原文 https://betterprogramming.pub/webview-javascript-injection-with-user-scripts-flutter-inappwebview-6-46d9969353a4
前言
了解如何利用 Flutter 的 InAppWebView 6 插件
有时你可能想在加载网页之前或之后注入 JavaScript 代码,以便从网页中添加、替换或删除内容,或者更改某些网页逻辑。
在本文中,我们将了解什么是用户脚本,以及如何使用它们在 WebView 中的特定时间使用 flutter_inappwebview 插件注入自定义 JavaScript 代码。
正文
UserScript 类
UserScript 类与 WKUserScript 本机类等效。它表示一个 JavaScript 代码,WebView 将其注入到网页以及任何其他后续的导航网页中。
使用 User Scripts 而不是仅仅注入一些 JavaScript 代码(例如,evaluateJavascript 方法)有什么好处?
UserScript 允许您在加载网页的其他资源之前注入 JavaScript 代码,并将 injectionTime 属性设置为 UserScriptInjectionTime.AT_DOCUMENT_START。
对于每个 UserScript,您可以设置一个可选的 Content World,设置是否使用 forMainFrameOnly 属性(仅适用于 iOS/macOS)将脚本注入主框架,以及一组允许起源的匹配规则(仅适用于 Android)。
安卓的注意事项
不幸的是,在 Android 上,当使用 UserScriptInjectionTime.AT_DOCUMENT_START 时,如果不支持 WebViewFeature.DOCUMENT_START_SCRIPT,就不能保证 JavaScript 代码在加载其他资源之前已经被注入,因为相应的本地类/特性不存在,所以 InAppWebView 试图尽快注入那个 UserScript。
要向 WebView 添加 UserScript,可以使用 WebView.initialUserScripts 属性:
InAppWebView(
initialUrlRequest: URLRequest(url: WebUri('https://flutter.dev')),
initialUserScripts: UnmodifiableListView<UserScript>([
UserScript(
source: "var foo = 49;",
injectionTime: UserScriptInjectionTime.AT_DOCUMENT_START),
UserScript(
source: "var bar = 2;",
injectionTime: UserScriptInjectionTime.AT_DOCUMENT_END),
]),
onLoadStop: (controller, url) async {
var result = await controller.evaluateJavascript(source: "foo + bar");
print(result); // 51
},
),
对于每个 UserScript,您可以设置以下属性:
- GroupName: 脚本的组名。
- Source: 脚本的源代码。
- InjectionTime: 将脚本注入 WebView 的时间。它可以是 UserScriptInjectionTime.AT_DOCUMENT_START 或 UserScriptInjectionTime.AT_DOCUMENT_END。
- ForMainFrameOnly: 一个布尔值,指示是否将脚本注入主框架。指定 true 只将脚本注入到主框架中,或者指定 false 将脚本注入到所有框架中。默认值为 true。
- AllowedOriginRules: 允许起源的一组匹配规则。只能在 Android 上使用,并且只能在支持 WebViewFeature.DOCUMENT_START_SCRIPT 特性的情况下使用。
- ContentWorld: 执行范围,用于评估脚本以防止不同脚本之间的冲突。
检查每个特定属性的代码文档,以了解哪个平台支持该特性。
要在运行时添加或删除 User Scripts,还可以使用相应的方法,如 InAppWebViewController.addUserScript、 InAppWebViewController.RemoveUserScript 等。
请注意,在运行时添加或删除用户脚本,在网页加载后,将不会生效,直到下一个网页加载。
此外,对于每个 UserScript,您可以定义一个 groupName,例如,可以使用 InAppWebViewController.RemoveUserScriptsByGroupName 方法删除一组用户脚本。
这是对向 WebView 添加基本用户脚本的一个简单概述。
但是,用户脚本可以做什么呢?
它可以做任何一个普通脚本在网页上可以做的事情,比如修改 HTML 文档结构,监听 onload 等事件,加载外部资源(图像,XMLHttpRequest,获取,等等。.).你也可以通过 Flutter/Dart 端进行回传(查看官方的 JavaScript 通信文档了解更多信息)。
让我们用一个例子把我们学到的东西付诸实践吧!
用户脚本示例
在这个例子中,我们加载了 3 个自定义用户脚本的 https://example.com 网页,以改变一些 CSS 样式,并使用 javascript 处理程序为 Flutter/Dart side 的双向通信添加一些逻辑。
import 'dart:async';
import 'dart:collection';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
final userScript1 = UserScript(
groupName: "myUserScripts",
source: ""
"
window.addEventListener('load', function(event) {
document.body.style.backgroundColor = 'blue';
document.body.style.padding = '20px';
});
""",
injectionTime: UserScriptInjectionTime.AT_DOCUMENT_START);
final userScript2 = UserScript(
groupName: "myUserScripts",
source: ""
"
var h1 = document.querySelector('h1');
h1.addEventListener('click', function(event) {
window.flutter_inappwebview.callHandler('h1Click', h1.innerText);
});
""",
injectionTime: UserScriptInjectionTime.AT_DOCUMENT_END);
final userScript3 = UserScript(
groupName: "myUserScripts",
source: "document.querySelector('h1').innerHTML = 'Custom Title';",
injectionTime: UserScriptInjectionTime.AT_DOCUMENT_END);
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
if (!kIsWeb &&
kDebugMode &&
defaultTargetPlatform == TargetPlatform.android) {
await InAppWebViewController.setWebContentsDebuggingEnabled(kDebugMode);
}
runApp(const MaterialApp(home: MyApp()));
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final GlobalKey webViewKey = GlobalKey();
InAppWebViewController? webViewController;
void handleClick(int item) async {
switch (item) {
case 0:
await webViewController?.removeAllUserScripts();
break;
case 1:
await webViewController?.addUserScript(userScript: userScript1);
break;
case 2:
await webViewController?.removeUserScript(userScript: userScript1);
break;
case 3:
await webViewController?.addUserScript(userScript: userScript2);
break;
case 4:
await webViewController?.removeUserScript(userScript: userScript2);
break;
case 5:
await webViewController?.addUserScript(userScript: userScript3);
break;
case 6:
await webViewController?.removeUserScript(userScript: userScript3);
break;
}
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("User Scripts"),
actions: <Widget>[
IconButton(
onPressed: () {
webViewController?.reload();
},
icon: const Icon(Icons.refresh)),
PopupMenuButton<int>(
onSelected: (item) => handleClick(item),
itemBuilder: (context) => [
const PopupMenuItem<int>(
value: 0, child: Text('Remove User Scripts')),
const PopupMenuItem<int>(
value: 1, child: Text('Add User Script 1')),
const PopupMenuItem<int>(
value: 2, child: Text('Remove User Script 1')),
const PopupMenuItem<int>(
value: 3, child: Text('Add User Script 2')),
const PopupMenuItem<int>(
value: 4, child: Text('Remove User Script 2')),
const PopupMenuItem<int>(
value: 5, child: Text('Add User Script 3')),
const PopupMenuItem<int>(
value: 6, child: Text('Remove User Script 3')),
],
),
],
),
body: Column(children: <Widget>[
Expanded(
child: Stack(
children: [
InAppWebView(
key: webViewKey,
initialUrlRequest:
URLRequest(url: WebUri('https://example.com')),
initialUserScripts: UnmodifiableListView<UserScript>(
[userScript1, userScript2, userScript3]),
onWebViewCreated: (controller) {
webViewController = controller;
controller.addJavaScriptHandler(
handlerName: 'h1Click',
callback: (arguments) {
final String h1InnerText = arguments[0];
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('h1 clicked'),
content: Text(h1InnerText),
);
},
);
});
},
),
],
),
),
]));
}
}
使用右上角的操作按钮删除或添加用户脚本,然后重新加载网页以使更改生效。
结果如下:
WebView User Scripts example.
WebView 用户脚本示例。
WebView 提供了强大的工具来操作 JavaScript,用户脚本特性就是其中之一。
结束语
如果本文对你有帮助,请转发让更多的朋友阅读。
也许这个操作只要你 3 秒钟,对我来说是一个激励,感谢。
祝你有一个美好的一天~
© 猫哥
微信 ducafecat
https://wiki.ducafecat.tech
https://ducafecat.com