Skip to content

Commit 796dc17

Browse files
committed
[webview] Add new method for cross plugin webview access
A new way of supporting cross plugin communication is coming in Flutter 3.44 which allows for access to plugin published values through the `FlutterPluginBinding` on Android and the `FlutterPluginRegistry` on iOS. Update native WebView access to support these new methods and deprecate the old methods. refs: flutter/flutter#121527 flutter/flutter#182753 # Conflicts: # packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml
1 parent 0ffbde8 commit 796dc17

8 files changed

Lines changed: 135 additions & 23 deletions

File tree

packages/webview_flutter/webview_flutter_android/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 4.13.0
2+
3+
* Adds new method for accessing native `WebView` from a `FlutterPluginBinding`.
4+
15
## 4.12.0
26

37
* Adds support for retrieving cookies with `PlatformWebViewCookieManager.getCookies`.

packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterAndroidExternalApi.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import androidx.annotation.NonNull;
99
import androidx.annotation.Nullable;
1010
import io.flutter.embedding.engine.FlutterEngine;
11+
import io.flutter.embedding.engine.plugins.FlutterPlugin;
1112

1213
/**
1314
* App and package facing native API provided by the `webview_flutter_android` plugin.
@@ -27,13 +28,42 @@ public interface WebViewFlutterAndroidExternalApi {
2728
* <p>See the Dart method `AndroidWebViewController.webViewIdentifier` to get the identifier of an
2829
* underlying `WebView`.
2930
*
31+
* @param binding the plugin binding provided by the Flutter engine. If the binding doesn't
32+
* contain an attached instance of {@link WebViewFlutterPlugin}, this method returns null.
33+
* @param identifier the associated identifier of the `WebView`.
34+
* @return the `WebView` associated with `identifier` or null if a `WebView` instance associated
35+
* with `identifier` could not be found.
36+
*/
37+
@Nullable
38+
static WebView getWebView(@NonNull FlutterPlugin.FlutterPluginBinding binding, long identifier) {
39+
final WebViewFlutterPlugin webViewPlugin =
40+
(WebViewFlutterPlugin) binding.getPlugin(WebViewFlutterPlugin.class);
41+
42+
if (webViewPlugin != null && webViewPlugin.getInstanceManager() != null) {
43+
final Object instance = webViewPlugin.getInstanceManager().getInstance(identifier);
44+
if (instance instanceof WebView) {
45+
return (WebView) instance;
46+
}
47+
}
48+
49+
return null;
50+
}
51+
52+
/**
53+
* Retrieves the {@link WebView} that is associated with `identifier`.
54+
*
55+
* <p>See the Dart method `AndroidWebViewController.webViewIdentifier` to get the identifier of an
56+
* underlying `WebView`.
57+
*
58+
* @deprecated Use {@link #getWebView(FlutterPlugin.FlutterPluginBinding, long)} instead.
3059
* @param engine the execution environment the {@link WebViewFlutterPlugin} should belong to. If
3160
* the engine doesn't contain an attached instance of {@link WebViewFlutterPlugin}, this
3261
* method returns null.
3362
* @param identifier the associated identifier of the `WebView`.
3463
* @return the `WebView` associated with `identifier` or null if a `WebView` instance associated
3564
* with `identifier` could not be found.
3665
*/
66+
@Deprecated
3767
@Nullable
3868
static WebView getWebView(@NonNull FlutterEngine engine, long identifier) {
3969
final WebViewFlutterPlugin webViewPlugin =

packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewFlutterAndroidExternalApiTest.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import static org.junit.Assert.assertEquals;
88
import static org.junit.Assert.assertNotNull;
9+
import static org.junit.Assert.assertNull;
910
import static org.mockito.Mockito.mock;
1011
import static org.mockito.Mockito.when;
1112

@@ -33,6 +34,7 @@ public class WebViewFlutterAndroidExternalApiTest {
3334

3435
@Mock FlutterPlugin.FlutterPluginBinding mockPluginBinding;
3536

37+
@SuppressWarnings("deprecation")
3638
@Test
3739
public void getWebView() {
3840
final WebViewFlutterPlugin webViewFlutterPlugin = new WebViewFlutterPlugin();
@@ -60,4 +62,34 @@ public void getWebView() {
6062

6163
webViewFlutterPlugin.onDetachedFromEngine(mockPluginBinding);
6264
}
65+
66+
@Test
67+
public void getWebViewFromBinding() {
68+
final WebViewFlutterPlugin webViewFlutterPlugin = new WebViewFlutterPlugin();
69+
70+
when(mockPluginBinding.getApplicationContext()).thenReturn(mockContext);
71+
when(mockPluginBinding.getPlatformViewRegistry()).thenReturn(mockViewRegistry);
72+
when(mockPluginBinding.getBinaryMessenger()).thenReturn(mockBinaryMessenger);
73+
74+
webViewFlutterPlugin.onAttachedToEngine(mockPluginBinding);
75+
76+
final AndroidWebkitLibraryPigeonInstanceManager instanceManager =
77+
webViewFlutterPlugin.getInstanceManager();
78+
assertNotNull(instanceManager);
79+
80+
final WebView mockWebView = mock(WebView.class);
81+
instanceManager.addDartCreatedInstance(mockWebView, 0);
82+
83+
when(mockPluginBinding.getPlugin(WebViewFlutterPlugin.class)).thenReturn(webViewFlutterPlugin);
84+
85+
assertEquals(WebViewFlutterAndroidExternalApi.getWebView(mockPluginBinding, 0), mockWebView);
86+
87+
webViewFlutterPlugin.onDetachedFromEngine(mockPluginBinding);
88+
}
89+
90+
@Test
91+
public void getWebViewFromBindingReturnsNullForUnknownPlugin() {
92+
when(mockPluginBinding.getPlugin(WebViewFlutterPlugin.class)).thenReturn(null);
93+
assertNull(WebViewFlutterAndroidExternalApi.getWebView(mockPluginBinding, 0));
94+
}
6395
}

packages/webview_flutter/webview_flutter_android/pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ name: webview_flutter_android
22
description: A Flutter plugin that provides a WebView widget on Android.
33
repository: https://github.com/flutter/packages/tree/main/packages/webview_flutter/webview_flutter_android
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22
5-
version: 4.12.0
5+
version: 4.13.0
66

77
environment:
88
sdk: ^3.9.0
9-
flutter: ">=3.35.0"
9+
flutter: ">=3.44.0"
1010

1111
flutter:
1212
plugin:

packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 3.26.0
2+
3+
* Adds new method for accessing native `WKWebViews` from a `FlutterPluginRegistrar`.
4+
15
## 3.25.1
26

37
* Relands update to prevent message calls when application will terminate.

packages/webview_flutter/webview_flutter_wkwebview/darwin/Tests/FWFWebViewFlutterWKWebViewExternalAPITests.swift

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,11 @@ class FWFWebViewFlutterWKWebViewExternalAPITests: XCTestCase {
2727

2828
WebViewFlutterPlugin.register(with: registrar)
2929

30-
let plugin = registry.registrar.plugin as! WebViewFlutterPlugin?
30+
let plugin = registry.registrar.publishedValue as! WebViewFlutterPlugin
3131

3232
let webView = WKWebView(frame: .zero)
3333
let webViewIdentifier = 0
34-
plugin?.proxyApiRegistrar?.instanceManager.addDartCreatedInstance(
34+
plugin.proxyApiRegistrar?.instanceManager.addDartCreatedInstance(
3535
webView, withIdentifier: Int64(webViewIdentifier))
3636

3737
let result = FWFWebViewFlutterWKWebViewExternalAPI.webView(
@@ -40,26 +40,49 @@ class FWFWebViewFlutterWKWebViewExternalAPITests: XCTestCase {
4040
}
4141

4242
@MainActor func testWebViewForIdentifierHandlesIncorrectRegistry() {
43-
let registry = TestRegistry(publishedValue: false)
43+
let registry = TestRegistry()
4444
// Ensure that passing an empty registry, such as the FlutterAppDelegate
4545
// in an app that has adopted UIScene, gracefully returns nil.
4646
let result = FWFWebViewFlutterWKWebViewExternalAPI.webView(
4747
forIdentifier: 0, withPluginRegistry: registry)
4848
XCTAssertEqual(result, nil)
4949
}
50-
}
5150

52-
class TestRegistry: NSObject, FlutterPluginRegistry {
53-
let registrar = TestFlutterPluginRegistrar()
54-
let publishedValue: Bool
51+
@MainActor func testWebViewForIdentifierFromRegistrar() {
52+
let registry = TestRegistry()
53+
54+
#if os(iOS)
55+
let registrar = registry.registrar(forPlugin: "")!
56+
#elseif os(macOS)
57+
let registrar = registry.registrar(forPlugin: "")
58+
#endif
59+
60+
WebViewFlutterPlugin.register(with: registrar)
61+
62+
let plugin = registry.registrar.publishedValue as! WebViewFlutterPlugin
63+
64+
let webView = WKWebView(frame: .zero)
65+
let webViewIdentifier = 0
66+
plugin.proxyApiRegistrar?.instanceManager.addDartCreatedInstance(
67+
webView, withIdentifier: Int64(webViewIdentifier))
5568

56-
init(publishedValue: Bool) {
57-
self.publishedValue = publishedValue
69+
let result = FWFWebViewFlutterWKWebViewExternalAPI.webView(
70+
forIdentifier: Int64(webViewIdentifier), withPluginRegistrar: registrar)
71+
XCTAssertEqual(result, webView)
5872
}
5973

60-
convenience override init() {
61-
self.init(publishedValue: true)
74+
@MainActor func testWebViewForIdentifierHandlesIncorrectRegistrar() {
75+
let registrar = TestFlutterPluginRegistrar()
76+
// Ensure that passing an empty registry, such as the FlutterAppDelegate
77+
// in an app that has adopted UIScene, gracefully returns nil.
78+
let result = FWFWebViewFlutterWKWebViewExternalAPI.webView(
79+
forIdentifier: 0, withPluginRegistrar: registrar)
80+
XCTAssertEqual(result, nil)
6281
}
82+
}
83+
84+
class TestRegistry: NSObject, FlutterPluginRegistry {
85+
let registrar = TestFlutterPluginRegistrar()
6386

6487
#if os(iOS)
6588
func registrar(forPlugin pluginKey: String) -> FlutterPluginRegistrar? {
@@ -76,10 +99,7 @@ class TestRegistry: NSObject, FlutterPluginRegistry {
7699
}
77100

78101
func valuePublished(byPlugin pluginKey: String) -> NSObject? {
79-
if publishedValue && pluginKey == "WebViewFlutterPlugin" {
80-
return registrar.plugin
81-
}
82-
return nil
102+
return registrar.publishedValue
83103
}
84104
}
85105

@@ -98,7 +118,7 @@ class TestFlutterTextureRegistry: NSObject, FlutterTextureRegistry {
98118
}
99119

100120
class TestFlutterPluginRegistrar: NSObject, FlutterPluginRegistrar {
101-
var plugin: WebViewFlutterPlugin? = nil
121+
var publishedValue: NSObject? = nil
102122

103123
#if os(iOS)
104124
var viewController: UIViewController?
@@ -144,7 +164,7 @@ class TestFlutterPluginRegistrar: NSObject, FlutterPluginRegistrar {
144164
}
145165

146166
func publish(_ value: NSObject) {
147-
plugin = (value as! WebViewFlutterPlugin)
167+
publishedValue = value
148168
}
149169

150170
func addMethodCallDelegate(_ delegate: FlutterPlugin, channel: FlutterMethodChannel) {
@@ -159,7 +179,10 @@ class TestFlutterPluginRegistrar: NSObject, FlutterPluginRegistrar {
159179
return ""
160180
}
161181

162-
func valuePublished(byPlugin: String) -> NSObject? {
182+
func valuePublished(byPlugin pluginKey: String) -> NSObject? {
183+
if pluginKey == "WebViewFlutterPlugin" {
184+
return publishedValue
185+
}
163186
return nil
164187
}
165188
}

packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/WebViewFlutterWKWebViewExternalAPI.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public class FWFWebViewFlutterWKWebViewExternalAPI: NSObject {
2424
///
2525
/// See the Dart method `WebKitWebViewController.webViewIdentifier` to get the identifier of an
2626
/// underlying `WKWebView`.
27+
@available(*, deprecated, message: "Use webView(forIdentifier:withPluginRegistrar:) instead.")
2728
@objc(webViewForIdentifier:withPluginRegistry:)
2829
public static func webView(
2930
forIdentifier identifier: Int64, withPluginRegistry registry: FlutterPluginRegistry
@@ -37,4 +38,22 @@ public class FWFWebViewFlutterWKWebViewExternalAPI: NSObject {
3738
forIdentifier: identifier)
3839
return webView
3940
}
41+
42+
/// Retrieves the `WKWebView` that is associated with `identifier` using a FlutterPluginRegistrar
43+
///
44+
/// See the Dart method `WebKitWebViewController.webViewIdentifier` to get the identifier of an
45+
/// underlying `WKWebView`.
46+
@objc(webViewForIdentifier:withPluginRegistrar:)
47+
public static func webView(
48+
forIdentifier identifier: Int64, withPluginRegistrar registrar: FlutterPluginRegistrar
49+
) -> WKWebView? {
50+
let plugin = registrar.valuePublished(byPlugin: "WebViewFlutterPlugin")
51+
guard let webviewPlugin = plugin as? WebViewFlutterPlugin else {
52+
return nil
53+
}
54+
55+
let webView: WKWebView? = webviewPlugin.proxyApiRegistrar?.instanceManager.instance(
56+
forIdentifier: identifier)
57+
return webView
58+
}
4059
}

packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ name: webview_flutter_wkwebview
22
description: A Flutter plugin that provides a WebView widget based on Apple's WKWebView control.
33
repository: https://github.com/flutter/packages/tree/main/packages/webview_flutter/webview_flutter_wkwebview
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22
5-
version: 3.25.1
5+
version: 3.26.0
66

77
environment:
8-
sdk: ^3.10.0
9-
flutter: ">=3.38.0"
8+
sdk: ^3.9.0
9+
flutter: ">=3.44.0"
1010

1111
flutter:
1212
plugin:

0 commit comments

Comments
 (0)