使用 Flutter InAppWebView 创建 WebView 内容拦截器

  • 发表于
  • flutter

在本文中,我们将学习如何使用插件 flutter_inappwebview 为我们的 WebView 实例创建自定义内容拦截器。

内容拦截器通常用于拦截广告,但您也可以使用它们来拦截任何其他内容。阻止行为包括隐藏元素、阻止加载,以及在 iOS 和 macOS 上从 WebView 请求中剥离 Cookie。

请记住,一般来说,内容拦截器无法实现与 AdBlock 或 AdBlock Plus 等专用扩展程序相同级别的功能。内容阻止程序是一组规则,当 WebView 找到需要阻止的内容时,它们永远不会收到来自 WebView 的任何回调或通知。

通过 InAppWebViewSettings 类的 contentBlockers 属性,我们可以定义 WebView 将使用的 ContentBlocker 实例列表。

ContentBlocker 类

我们在 ContentBlocker 类中定义内容阻止行为。每个属性都包含一个 action 属性和一个 trigger 属性。该操作告诉 WebView 在遇到触发器匹配项时要执行的操作。触发器告诉 WebView 何时执行相应的操作。

下面是一个基本示例:

initialSettings: InAppWebViewSettings(contentBlockers: [
 ContentBlocker(
 trigger: ContentBlockerTrigger(
 urlFilter: ".*",
 resourceType: [
 ContentBlockerTriggerResourceType.IMAGE,
 ContentBlockerTriggerResourceType.STYLE_SHEET
 ]
 ),
 action: ContentBlockerAction(
 type: ContentBlockerActionType.BLOCK
 )
 )
]),

在此示例中,ContentBlocker 阻止加载每个 URL 的每个图像和样式表。

将触发器添加到内容拦截器

触发器必须定义必需 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

  • urlFilterIsCaseSensitive :如果 URL 匹配应区分大小写。默认情况下,它不区分大小写。
  • resourceType :表示规则应匹配的资源类型(浏览器打算如何使用资源)的“ContentBlockerTriggerResourceType”列表。如果未指定,则规则将匹配所有资源类型。
  • ifDomain :与URL的域匹配的字符串列表;它将操作限制为特定域的列表。对于非 ASCII 字符,值必须为小写 ASCII 或 Punycode。在前面添加 * 以匹配域和子域。它不能与 unlessDomain 一起使用。
  • unlessDomain :与URL的域匹配的字符串列表;作用于除所提供列表中的域之外的任何站点。对于非 ASCII,值必须为小写 ASCII 或 Punycode。在前面添加 * 以匹配域和子域。它不能与 ifDomain 一起使用。
  • loadType : ContentBlockerTriggerLoadType 该列表可以包含两个互斥值之一。如果未指定,则规则将匹配所有荷载类型。 ContentBlockerTriggerLoadType.FIRST_PARTY 仅当资源与主页资源具有相同的方案、域和端口时才会触发。 ContentBlockerTriggerLoadType.THIRD_PARTY 如果资源与主页资源不来自同一域,则触发。
  • ifTopUrl :与整个主文档 URL 匹配的字符串列表;它将操作限制为特定的 URL 模式列表。对于非 ASCII 字符,值必须为小写 ASCII 或 Punycode。它不能与 unlessTopUrl 一起使用。
  • unlessTopUrl :与整个主文档 URL 匹配的字符串数组;它作用于除所提供列表中的 URL 模式之外的任何站点。对于非 ASCII 字符,值必须为小写 ASCII 或 Punycode。它不能与 ifTopUrl 一起使用。
  • loadContext :指定加载上下文的字符串数组。
  • ifFrameUrl :用于匹配 iframe 的 URL 的正则表达式列表。

查看每个特定属性的代码文档,了解哪个平台支持该功能。

向内容拦截器添加操作

当触发器与资源匹配时,WebView 会评估所有触发器并按顺序执行操作。

将具有类似操作的规则组合在一起以提高性能。例如,首先指定阻止内容加载的规则,然后指定阻止 Cookie 的规则。

操作只有两个有效属性: type 和 selector 。操作类型是必需的。

如果类型为 ContentBlockerActionType.CSS_DISPLAY_NONE ,则 a selector 也是必需的;否则,是可选的 selector 。

下面是一个简单的示例:

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。这只会阻止 WebView 的隐私政策可接受的 cookie。结合 BLOCK_COOKIES 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);

 @override
 State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
 final GlobalKey webViewKey = GlobalKey();

 // list of ad URL filters to be used to block ads from 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;

 @override
 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")));
 }

 @override
 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;
 },
 ),
 ],
 ),
 ),
 ])));
 }
}

使用这些规则将阻止大量广告出现,例如 Google Ads。

单击“禁用/启用”按钮以禁用或启用广告拦截器功能。

使用 Flutter InAppWebView 创建 WebView 内容拦截器

结论

内容拦截器允许我们编写高性能规则来阻止 WebView 中的内容,同时尊重用户的隐私。

GitHub 上提供了完整的项目代码。这就是今天的全部内容 via