Flutter WebView 内容拦截器 ーー AppWebView 6
原文 https://medium.com/@pichillilorenzo/webview-content-blockers-flutter-inappwebview-6-faba4d61c294
前言
在本文中,我们将了解如何使用 flutter_inappwebview 插件为我们的 WebView 实例创建自定义 Content Block。
内容拦截器通常用于拦截广告,但你也可以拦截任何其他内容。拦截行为包括隐藏元素、拦截负载,以及在 iOS 和 macOS 上从 WebView 请求中剥离 cookie。
请记住,一般来说,Content Blockers 不能实现与 AdBlock 或 AdBlock Plus 等专用 extension 相同级别的功能。Content Blockers 是一组规则,当 WebView 找到需要阻止的内容时,它将永远不会从 WebView 获得任何回调或通知。
通过 InAppWebViewSettings 类的 contentBlockers 属性,我们可以定义 WebView 将使用的 ContentBlockers 实例的列表。
正文
ContentBlocker 内容过滤类
ContentBlock 类是我们定义内容拦截行为的类。每个属性包含一个操作属性和一个触发器属性。该操作告诉 WebView 在遇到与触发器匹配的对象时要做什么。触发器告诉 WebView 何时执行相应的操作。
下面是一个基本的例子:
initialSettings: InAppWebViewSettings(contentBlockers: [
ContentBlocker(
trigger: ContentBlockerTrigger(
urlFilter: ".*",
resourceType: [
ContentBlockerTriggerResourceType.IMAGE,
ContentBlockerTriggerResourceType.STYLE_SHEET
]
),
action: ContentBlockerAction(
type: ContentBlockerActionType.BLOCK
)
)
]),
在这个例子中,我们说的是为每个 URL 阻止每个图像和样式表的加载。
将 triggers 触发器添加到 Content Blocker 内容阻止器
触发器必须定义所需的 urlFilter 属性,该属性将正则表达式指定为与 URL 匹配的字符串。其他属性是可选的,可以修改触发器的行为。例如,可以将触发器限制为特定域,或者在 WebView 找到与特定域匹配的触发器时不应用触发器。
下面是一个内容拦截器的例子,它带有一个针对图像和样式表资源的触发器,WebView 可以在任何域中找到这些资源,但是指定的域除外:
initialSettings: InAppWebViewSettings(contentBlockers: [
ContentBlocker(
trigger: ContentBlockerTrigger(
urlFilter: ".*",
resourceType: [
ContentBlockerTriggerResourceType.IMAGE,
ContentBlockerTriggerResourceType.STYLE_SHEET
],
unlessDomain: ["example.com", "github.com", "pub.dev"]
),
action: ContentBlockerAction(
type: ContentBlockerActionType.BLOCK
)
)
]),
对于更深层次的触发器自定义,您可以使用 ContentBlockerTrigger 的其他属性:
- 如果 URL 匹配应该区分大小写。默认情况下,它是不区分大小写的。
- Resource ceType: “ ContentBlockerTriggerResourceType”的列表,表示规则应该匹配的资源类型(浏览器打算如何使用资源)。如果未指定,则该规则匹配所有资源类型。
- IfDomain: 与 URL 域匹配的字符串列表; 将操作限制为特定域的列表。值必须是小写的 ASCII,或非 ASCII 的小写字母代码。在前面添加 * 以匹配域和子域。它不能和“ unless Domain”一起使用。
- Unless 域名: 与 URL 域名匹配的字符串列表; 在提供列表中的域名以外的任何站点上执行操作。值必须是小写的 ASCII,或非 ASCII 的小写字母代码。在前面添加 * 以匹配域和子域。它不能与“ ifDomain”一起使用。
- LoadType: “ ContentBlockerTriggerLoadType”的列表,可以包括两个相互排斥的值之一。如果未指定,则该规则匹配所有加载类型。“ ContentBlockerTriggerLoadType.FIRST_PARTY”仅在资源与主页资源具有相同的方案、域和端口时触发。如果资源与主页资源来自不同的域,则“ ContentBlockerTriggerLoadType.THIRD_PARTY”触发器。
- IfTopUrl: 与整个主文档 URL 匹配的字符串列表; 将操作限制为 URL 模式的特定列表。值必须是小写的 ASCII,或非 ASCII 的小写字母代码。它不能和“ unless TopUrl”一起使用。
- Unless TopUrl: 与整个主文档 URL 匹配的字符串数组; 在所提供列表中的 URL 模式以外的任何站点上进行操作。值必须是小写的 ASCII,或非 ASCII 的小写字母代码。它不能与“ ifTopUrl”一起使用。
- LoadContext: 指定加载上下文的字符串数组。
- IfFrameUrl: 用于匹配 iframe URL 的正则表达式列表。
检查每个特定属性的代码文档,以了解哪个平台支持该特性。
向内容屏蔽器添加 actions 操作
当触发器与资源匹配时,WebView 计算所有触发器并按顺序执行操作。
将具有相似操作的规则组合在一起以提高性能。例如,首先指定阻止内容加载的规则,然后指定阻止 Cookie 的规则。
操作只有两个有效属性: type 和 selector。操作类型是必需的。 如果类型是 ContentBlockerActionType.CSS_DISPLAY_NONE,也需要一个选择器; 否则,选择器是可选的。
下面是一个简单的例子:
initialSettings: InAppWebViewSettings(contentBlockers: [
ContentBlocker(
trigger: ContentBlockerTrigger(
urlFilter: "https://flutter.dev/.*",
),
action: ContentBlockerAction(
type: ContentBlockerActionType.CSS_DISPLAY_NONE,
selector: '.notification, .media, #developer-story'
)
)
]),
有效类型包括:
- BLOCK: 停止加载资源。如果资源被缓存,缓存将被忽略。
- BLOCK_COOKIES: 在将 Cookie 发送到服务器之前,从头中剥离 Cookie。这只能阻止 Cookie,否则 WebView 的隐私政策是可以接受的。结合“ IGNORE_PREVIOUS_RULES”不会覆盖浏览器的隐私设置。
- CSS_DISPLAY_NONE: 基于 CSS 选择器隐藏页面元素。选择器字段包含选择器列表。任何匹配的元素都将其 display 属性设置为 none,这将隐藏它。
- MAKE_HTTPS: 将 URL 从 http 更改为 https。具有指定(非默认)端口的 URL 和使用其他协议的链接不受影响。
- IGNORE_PREVIOUS_RULES: 忽略以前触发的动作。
检查每个特定类型的代码文档,以了解哪个平台支持它。
创建一个简单的广告屏蔽器
让我们用我们学到的知识创建一个简单的广告拦截器。
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
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);
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final GlobalKey webViewKey = GlobalKey();
// list of Ad URL filters to be used to block ads loading.
final adUrlFilters = [
".*.doubleclick.net/.*",
".*.ads.pubmatic.com/.*",
".*.googlesyndication.com/.*",
".*.google-analytics.com/.*",
".*.adservice.google.*/.*",
".*.adbrite.com/.*",
".*.exponential.com/.*",
".*.quantserve.com/.*",
".*.scorecardresearch.com/.*",
".*.zedo.com/.*",
".*.adsafeprotected.com/.*",
".*.teads.tv/.*",
".*.outbrain.com/.*"
];
final List<ContentBlocker> contentBlockers = [];
var contentBlockerEnabled = true;
InAppWebViewController? webViewController;
void initState() {
super.initState();
// for each Ad URL filter, add a Content Blocker to block its loading.
for (final adUrlFilter in adUrlFilters) {
contentBlockers.add(ContentBlocker(
trigger: ContentBlockerTrigger(
urlFilter: adUrlFilter,
),
action: ContentBlockerAction(
type: ContentBlockerActionType.BLOCK,
)));
}
// apply the "display: none" style to some HTML elements
contentBlockers.add(ContentBlocker(
trigger: ContentBlockerTrigger(
urlFilter: ".*",
),
action: ContentBlockerAction(
type: ContentBlockerActionType.CSS_DISPLAY_NONE,
selector: ".banner, .banners, .ads, .ad, .advert")));
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Ads Content Blocker"),
actions: [
TextButton(
onPressed: () async {
contentBlockerEnabled = !contentBlockerEnabled;
if (contentBlockerEnabled) {
await webViewController?.setSettings(
settings: InAppWebViewSettings(
contentBlockers: contentBlockers));
} else {
await webViewController?.setSettings(
settings: InAppWebViewSettings(contentBlockers: []));
}
webViewController?.reload();
setState(() {});
},
style: TextButton.styleFrom(foregroundColor: Colors.white),
child: Text(contentBlockerEnabled ? 'Disable' : 'Enable'),
)
],
),
body: SafeArea(
child: Column(children: <Widget>[
Expanded(
child: Stack(
children: [
InAppWebView(
key: webViewKey,
initialUrlRequest:
URLRequest(url: WebUri('https://www.tomshardware.com/')),
initialSettings:
InAppWebViewSettings(contentBlockers: contentBlockers),
onWebViewCreated: (controller) {
webViewController = controller;
},
),
],
),
),
])));
}
}
使用这些规则可以防止出现大量的广告,比如谷歌广告。
单击禁用/启用按钮禁用或启用广告屏蔽功能。
WebView 广告屏蔽器示例。
代码
https://github.com/ducafecat/flutter_inappwebview_examples
结束语
如果本文对你有帮助,请转发让更多的朋友阅读。
也许这个操作只要你 3 秒钟,对我来说是一个激励,感谢。
祝你有一个美好的一天~
© 猫哥
微信 ducafecat
https://wiki.ducafecat.tech
https://ducafecat.com