基于 WKWebView 的官方 iOS WebView 插件:webview_flutter_wkwebview 深度指南
2026/9/21 1:36:37 网站建设 项目流程

基于 WKWebView 的官方 iOS WebView 插件:webview_flutter_wkwebview 深度指南

【免费下载链接】pluginsPlugins for Flutter maintained by the Flutter team项目地址: https://gitcode.com/gh_mirrors/pl/plugins

webview_flutter_wkwebview是 Flutter 官方插件webview_flutter在 Apple 平台(iOS / macOS)上的 WKWebView 实现,属于官方联邦插件(federated plugin)中的平台实现包。本文以该包根目录的 README.md 为骨架,结合仓库源码与 iOS 原生类,系统讲解它在应用中的接入方式、endorsed 机制背后的注册原理、面向原生代码的 External Native API 用法,以及基于 pigeon/mockito 的二次开发与贡献流程,帮助你既能"开箱即用",也能在需要时深入到原生WKWebView层做定制。

一、包定位:Apple 平台上的 WKWebView 实现

根据 pubspec.yaml 的描述,这是一个"基于 Apple 的 WKWebView 控件提供 WebView Widget 的 Flutter 插件"。它并不是一个独立可用的包,而是webview_flutter在 iOS/macOS 上的官方平台实现:

  • 版本:3.1.0;
  • SDK 约束sdk: ">=2.17.0 <3.0.0"flutter: ">=3.0.0"
  • 插件声明implements: webview_flutterios.pluginClassFLTWebViewFlutterPlugindartPluginClassWebKitWebViewPlatform
  • 依赖webview_flutter_platform_interface ^2.0.0(平台接口抽象层)与path ^1.8.0

说明:当前仓库中的webview_flutter生态采用"主包(app-facing API)+ 平台接口包(抽象层)+ 各平台实现包"的联邦插件架构。本包即为抽象层WebViewPlatform在 Apple 平台上的具体实现。

二、接入方式:endorsed 插件与零配置使用

README 明确指出:该包是 endorsed(官方背书)插件,因此应用开发者无需直接依赖它,只需要正常使用webview_flutter主包即可:

dependencies: webview_flutter: ^4.0.0 # 以实际发布的版本为准

当应用依赖了webview_flutter后,本包会被自动引入,无需在pubspec.yaml中显式声明,也无需任何额外初始化代码——这正是 endorsed 联邦插件机制带来的体验:平台实现包通过implements: webview_flutter声明自己归属的接口包,由 Flutter 工具链在构建时自动按平台挑选正确的实现。

2.1 背后的注册机制:WebKitWebViewPlatform

从源码层面看,"零配置"的实质是平台实现包在初始化时向平台接口注册自己。核心入口位于 lib/src/webkit_webview_platform.dart:

/// Implementation of [WebViewPlatform] using the WebKit API. class WebKitWebViewPlatform extends WebViewPlatform { /// Registers this class as the default instance of [WebViewPlatform]. static void registerWith() { WebViewPlatform.instance = WebKitWebViewPlatform(); } @override WebKitWebViewController createPlatformWebViewController( PlatformWebViewControllerCreationParams params, ) => WebKitWebViewController(params); @override WebKitNavigationDelegate createPlatformNavigationDelegate( PlatformNavigationDelegateCreationParams params, ) => WebKitNavigationDelegate(params); @override WebKitWebViewWidget createPlatformWebViewWidget( PlatformWebViewWidgetCreationParams params, ) => WebKitWebViewWidget(params); @override WebKitWebViewCookieManager createPlatformCookieManager( PlatformWebViewCookieManagerCreationParams params, ) => WebKitWebViewCookieManager(params); }

registerWith()WebKitWebViewPlatform设为WebViewPlatform.instance全局默认实现,之后主包创建的任何 WebView 组件(控制器、导航委托、Widget、Cookie 管理器)都会经由这里的工厂方法落到 WebKit 原生实现上。该类的对外导出集中在 lib/webview_flutter_wkwebview.dart,它同时导出了WebKitWebViewControllerWebKitWebViewCookieManagerWebKitWebViewPlatform三个公开符号。

三、External Native API:在 iOS 原生代码中访问 WKWebView

README 中的核心技术亮点是插件提供的面向原生代码的外部 API(External Native API)。常规情况下,Flutter 开发者只通过 Dart API 与 WebView 交互;但当你的 App 或插件需要在 iOS 原生层对 WKWebView 做深度定制(例如叠加原生手势、接入原生广告 SDK、与原生 JS 桥接)时,可以通过这套 API 拿到真实的WKWebView实例。

3.1 访问约定与破坏性变更承诺

该 API 遵循一个重要的兼容性约定(README 原文要点):

  • 外部 API 与 Dart API 共享破坏性变更(breaking change)约定:任何不向后兼容的类变更,只会伴随插件major 版本升级而发生;
  • 除外部 API 之外的其余原生代码不遵循破坏性变更约定,因此 App 或插件客户端不应使用任何其他原生 API

换句话说,仓库ios/Classes/下绝大多数类(各*HostApiFWFInstanceManager等)都属于内部实现,随时可能变化;只有FWFWebViewFlutterWKWebViewExternalAPI是受版本约定保护的公开契约。

3.2 导入方式与核心类

在 Objective-C 代码中通过模块导入即可:

@import webview_flutter_wkwebview;

导入后即可访问原生类FWFWebViewFlutterWKWebViewExternalAPI。该类声明于 ios/Classes/FWFWebViewFlutterWKWebViewExternalAPI.h,核心方法如下:

@interface FWFWebViewFlutterWKWebViewExternalAPI : NSObject /** * Retrieves the `WKWebView` that is associated with `identifier`. * * @param identifier The associated identifier of the `WebView`. * @param registry The plugin registry the `FLTWebViewFlutterPlugin` should belong to. * @return The `WKWebView` associated with `identifier` or nil if not found. */ + (nullable WKWebView *)webViewForIdentifier:(long)identifier withPluginRegistry:(id<FlutterPluginRegistry>)registry; @end

参数含义:

参数类型说明
identifierlong与底层 WKWebView 关联的标识符,可在 Dart 侧通过WebKitWebViewController.webViewIdentifier获取
registryid<FlutterPluginRegistry>插件注册表;若其中未挂载FLTWebViewFlutterPlugin实例,方法返回nil
返回值WKWebView *(可空)identifier关联的 WKWebView;找不到时返回nil

3.3 Dart 侧如何拿到 identifier

在 Flutter/Dart 侧,先创建WebKitWebViewController,然后通过其webViewIdentifiergetter 拿到标识符。该 getter 定义于 lib/src/webkit_webview_controller.dart:

/// Identifier used to retrieve the underlying native `WKWebView`. /// /// This is typically used by other plugins to retrieve the native `WKWebView` /// from an `FWFInstanceManager`. int get webViewIdentifier => _webKitParams._instanceManager.getIdentifier(_webView)!;

结合原生实现(ios/Classes/FWFWebViewFlutterWKWebViewExternalAPI.m),完整的调用链为:

  1. Dart 侧WebKitWebViewController通过InstanceManager为底层WKWebView分配一个递增的整型 identifier;
  2. webViewIdentifier将该 identifier 暴露给上层调用者;
  3. 原生侧FWFWebViewFlutterWKWebViewExternalAPI.webViewForIdentifier:withPluginRegistry:从插件注册表取出FWFInstanceManager,用同一 identifier 反查原生对象:
    FWFInstanceManager *instanceManager = (FWFInstanceManager *)[registry valuePublishedByPlugin:@"FLTWebViewFlutterPlugin"]; id instance = [instanceManager instanceForIdentifier:identifier]; if ([instance isKindOfClass:[WKWebView class]]) { return instance; } return nil;
  4. 校验实例类型为WKWebView后返回给原生调用方。

3.4 典型应用场景示例

假设你需要在 iOS 原生层给 WebView 注入一段自定义 WKUserScript,大致流程如下:

// 1. 从 Dart 侧通过 MethodChannel/EventChannel 把 identifier 传递到原生层 long identifier = ...; // 来自 WebKitWebViewController.webViewIdentifier // 2. 通过 External Native API 获取底层 WKWebView WKWebView *webView = [FWFWebViewFlutterWKWebViewExternalAPI webViewForIdentifier:identifier withPluginRegistry:self.registrar]; if (webView == nil) { // 插件尚未注册或 identifier 无效 return; } // 3. 直接操作原生 WKWebView(示例:注入用户脚本) WKUserScript *script = [[WKUserScript alloc] initWithSource:@"..." injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO]; [webView.configuration.userContentController addUserScript:script];

需要说明的是:该特性自3.1.0版本引入(见 CHANGELOG.md 首条 "Adds support to access nativeWKWebView"),使用时请确认你的依赖解析到了该版本及以上。

四、源码结构:从 Dart 到原生 Host API

理解本包源码布局,有助于评估哪些内容属于受保护的外部 API,哪些属于随时可能变化的内部实现。仓库目录结构如下:

packages/webview_flutter/webview_flutter_wkwebview/ ├── lib/ # Dart 侧实现 │ ├── webview_flutter_wkwebview.dart # 公开导出 │ └── src/ │ ├── common/ # InstanceManager、pigeon 生成的 g.dart │ ├── foundation/ # Foundation 框架 API 实现 │ ├── ui_kit/ # UIKit 框架 API 实现 │ ├── web_kit/ # WebKit API 实现 │ └── webkit_*.dart # 平台实现(controller/cookie_manager/widget) ├── ios/Classes/ # 原生实现(全部 .h/.m) ├── pigeons/web_kit.dart # pigeon 通信接口定义(通信层唯一真源) ├── example/ # 示例工程(含 RunnerTests 原生单测) └── test/ # Dart 单测(含 mockito 生成的 .mocks.dart)

ios/Classes/下的原生类大致分三类:

  • 公开外部 APIFWFWebViewFlutterWKWebViewExternalAPI(唯一受版本约定保护);
  • pigeon 生成的 Host APIFWFWebViewHostApiFWFNavigationDelegateHostApiFWFWebViewConfigurationHostApiFWFHTTPCookieStoreHostApiFWFWebsiteDataStoreHostApiFWFUserContentControllerHostApiFWFScriptMessageHandlerHostApiFWFPreferencesHostApiFWFScrollViewHostApiFWFUIViewHostApiFWFUIDelegateHostApi等,均由pigeons/web_kit.dart生成(见 FWFGeneratedWebKitApis.h);
  • 基础设施FWFInstanceManager(原生对象与 identifier 的双向映射,见 FWFInstanceManager.h)、FWFDataConverters、插件入口FLTWebViewFlutterPlugin

五、二次开发与贡献:pigeon 与 mockito 代码生成流程

README 的 Contributing 部分说明了本包的两条关键开发工作流,对想要修改通信层或为包贡献代码的开发者非常实用。

5.1 修改通信接口后重新生成 pigeon 代码

本包使用 pigeon),Dart 侧的 lib/src/common/web_kit.g.dart 与原生侧的FWFGeneratedWebKitApis.h/.m都是由它生成的产物。

编辑完通信接口后,执行以下命令重新生成通信层:

flutter pub run pigeon --input pigeons/web_kit.dart

注:当前仓库的开发依赖锁定 pigeon^4.2.13(见 pubspec.yaml 的dev_dependencies),命令在包根目录下运行。修改接口后,一般还需要同步更新test/src/common/test_web_kit.g.dart对应的测试桩,可参照 test/src/common/instance_manager_test.dart 等既有测试的写法。

5.2 重新生成 mockito 测试替身

除 pigeon 外,本包还使用 mockito 为测试生成 mock 对象。修改了需要被 mock 的类(如WebKitProxy、各 delegate)后,运行:

flutter pub run build_runner build --delete-conflicting-outputs

生成结果即test/目录下的*.mocks.dart文件,例如 test/webkit_webview_controller_test.mocks.dart 与 test/webkit_navigation_delegate_test.mocks.dart。测试用例分布可参考:

  • test/webkit_webview_controller_test.dart:控制器行为;
  • test/webkit_webview_cookie_manager_test.dart:Cookie 管理;
  • test/webkit_webview_widget_test.dart:Widget 组装;
  • test/src/foundation/foundation_test.dart、test/src/ui_kit/ui_kit_test.dart、test/src/web_kit/web_kit_test.dart:分层 API 实现。

原生侧还有对应的 Objective-C 单元测试,位于 example/ios/RunnerTests/(如FWFNavigationDelegateHostApiTests.mFWFWebViewConfigurationHostApiTests.m),可直接验证各 Host API 与FWFInstanceManager的 identifier 传递行为。

5.3 原生侧工程信息

若需要集成或排查原生问题,可参考 podspec ios/webview_flutter_wkwebview.podspec 的关键配置:

  • source_files/public_header_filesClasses/**/*.{h,m},全部原生源码与公开头文件;
  • module_mapClasses/FlutterWebView.modulemap,支撑@import webview_flutter_wkwebview;的模块化导入;
  • platform:iOS 9.0 起;
  • pod_target_xcconfig:开启DEFINES_MODULE,并排除模拟器 i386 架构。

六、小结

webview_flutter_wkwebview是一个典型的 endorsed 联邦插件:应用侧零配置即可在 iOS/macOS 上获得基于 WKWebView 的完整 WebView 能力;而通过受版本约定保护的FWFWebViewFlutterWKWebViewExternalAPI,原生开发者还能安全地拿到底层WKWebView实例做深度定制。同时,其基于 pigeon 的通信层设计与 mockito 驱动的测试体系,也为维护者和贡献者提供了清晰的二次开发路径。若需了解 Dart 侧主包 API 的完整用法,可继续阅读 webview_flutter 包文档;若需自定义其他平台的实现,可参考同仓库下的webview_flutter_androidwebview_flutter_web等兄弟包。

【免费下载链接】pluginsPlugins for Flutter maintained by the Flutter team项目地址: https://gitcode.com/gh_mirrors/pl/plugins

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询