在本文中,我们将学习如何使用插件 flutter_inappwebview
为我们的 WebView 实例创建自定义内容拦截器。
内容拦截器通常用于拦截广告,但您也可以使用它们来拦截任何其他内容。阻止行为包括隐藏元素、阻止加载,以及在 iOS 和 macOS 上从 WebView 请求中剥离 Cookie。
请记住,一般来说,内容拦截器无法实现与 AdBlock 或 AdBlock Plus 等专用扩展程序相同级别的功能。内容阻止程序是一组规则,当 WebView 找到需要阻止的内容时,它们永远不会收到来自 WebView 的任何回调或通知。
通过 InAppWebViewSettings
类的 contentBlockers
属性,我们可以定义 WebView 将使用的 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' ) ) ]), |
有效类型包括:
查看每种特定类型的代码文档,了解哪个平台支持它。
让我们使用我们学到的知识创建一个简单的广告拦截器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
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。
单击“禁用/启用”按钮以禁用或启用广告拦截器功能。
内容拦截器允许我们编写高性能规则来阻止 WebView 中的内容,同时尊重用户的隐私。