Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit d2fba38

Browse files
[webview_flutter_android][webview_flutter_wkwebview] Adds support to retrieve native WebView (#7071)
* implementation of the webViewIdentifier field * change to external classes * formatting * update readmes * iOS * improve * hmmmm * add note about not using other apis * project changes * add external api tests to project * ordering * fix docs and use id
1 parent 66d5724 commit d2fba38

File tree

19 files changed

+311
-14
lines changed

19 files changed

+311
-14
lines changed

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+
## 3.3.0
2+
3+
* Adds support to access native `WebView`.
4+
15
## 3.2.4
26

37
* Renames Pigeon output files.

packages/webview_flutter/webview_flutter_android/README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,22 @@ This can be configured for versions >=23 with
3232
`AndroidWebViewWidgetCreationParams.displayWithHybridComposition`. See https://pub.dev/packages/webview_flutter#platform-specific-features
3333
for more details on setting platform-specific features in the main plugin.
3434

35+
### External Native API
36+
37+
The plugin also provides a native API accessible by the native code of Android applications or
38+
packages. This API follows the convention of breaking changes of the Dart API, which means that any
39+
changes to the class that are not backwards compatible will only be made with a major version change
40+
of the plugin. Native code other than this external API does not follow breaking change conventions,
41+
so app or plugin clients should not use any other native APIs.
42+
43+
The API can be accessed by importing the native class `WebViewFlutterAndroidExternalApi`:
44+
45+
Java:
46+
47+
```java
48+
import io.flutter.plugins.webviewflutter.WebViewFlutterAndroidExternalApi;
49+
```
50+
3551
## Contributing
3652

3753
This package uses [pigeon][3] to generate the communication layer between Flutter and the host
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
package io.flutter.plugins.webviewflutter;
6+
7+
import android.webkit.WebView;
8+
import androidx.annotation.Nullable;
9+
import io.flutter.embedding.engine.FlutterEngine;
10+
11+
/**
12+
* App and package facing native API provided by the `webview_flutter_android` plugin.
13+
*
14+
* <p>This class follows the convention of breaking changes of the Dart API, which means that any
15+
* changes to the class that are not backwards compatible will only be made with a major version
16+
* change of the plugin.
17+
*
18+
* <p>Native code other than this external API does not follow breaking change conventions, so app
19+
* or plugin clients should not use any other native APIs.
20+
*/
21+
@SuppressWarnings("unused")
22+
public interface WebViewFlutterAndroidExternalApi {
23+
/**
24+
* Retrieves the {@link WebView} that is associated with `identifier`.
25+
*
26+
* <p>See the Dart method `AndroidWebViewController.webViewIdentifier` to get the identifier of an
27+
* underlying `WebView`.
28+
*
29+
* @param engine the execution environment the {@link WebViewFlutterPlugin} should belong to. If
30+
* the engine doesn't contain an attached instance of {@link WebViewFlutterPlugin}, this
31+
* method returns null.
32+
* @param identifier the associated identifier of the `WebView`.
33+
* @return the `WebView` associated with `identifier` or null if a `WebView` instance associated
34+
* with `identifier` could not be found.
35+
*/
36+
@Nullable
37+
static WebView getWebView(FlutterEngine engine, long identifier) {
38+
final WebViewFlutterPlugin webViewPlugin =
39+
(WebViewFlutterPlugin) engine.getPlugins().get(WebViewFlutterPlugin.class);
40+
41+
if (webViewPlugin != null && webViewPlugin.getInstanceManager() != null) {
42+
final Object instance = webViewPlugin.getInstanceManager().getInstance(identifier);
43+
if (instance instanceof WebView) {
44+
return (WebView) instance;
45+
}
46+
}
47+
48+
return null;
49+
}
50+
}

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
* <p>Call {@link #registerWith} to use the stable {@code io.flutter.plugin.common} package instead.
3434
*/
3535
public class WebViewFlutterPlugin implements FlutterPlugin, ActivityAware {
36-
private InstanceManager instanceManager;
36+
@Nullable private InstanceManager instanceManager;
3737

3838
private FlutterPluginBinding pluginBinding;
3939
private WebViewHostApiImpl webViewHostApi;
@@ -148,7 +148,10 @@ public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
148148

149149
@Override
150150
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
151-
instanceManager.close();
151+
if (instanceManager != null) {
152+
instanceManager.close();
153+
instanceManager = null;
154+
}
152155
}
153156

154157
@Override
Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,16 @@
44

55
package io.flutter.plugins.webviewflutter;
66

7+
import static org.junit.Assert.assertEquals;
78
import static org.junit.Assert.assertNotNull;
9+
import static org.mockito.Mockito.mock;
810
import static org.mockito.Mockito.when;
911

1012
import android.content.Context;
13+
import android.webkit.WebView;
14+
import io.flutter.embedding.engine.FlutterEngine;
1115
import io.flutter.embedding.engine.plugins.FlutterPlugin;
16+
import io.flutter.embedding.engine.plugins.PluginRegistry;
1217
import io.flutter.plugin.common.BinaryMessenger;
1318
import io.flutter.plugin.platform.PlatformViewRegistry;
1419
import org.junit.Rule;
@@ -17,7 +22,7 @@
1722
import org.mockito.junit.MockitoJUnit;
1823
import org.mockito.junit.MockitoRule;
1924

20-
public class WebViewFlutterPluginTest {
25+
public class WebViewFlutterAndroidExternalApiTest {
2126
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
2227

2328
@Mock Context mockContext;
@@ -29,7 +34,7 @@ public class WebViewFlutterPluginTest {
2934
@Mock FlutterPlugin.FlutterPluginBinding mockPluginBinding;
3035

3136
@Test
32-
public void getInstanceManagerAfterOnAttachedToEngine() {
37+
public void getWebView() {
3338
final WebViewFlutterPlugin webViewFlutterPlugin = new WebViewFlutterPlugin();
3439

3540
when(mockPluginBinding.getApplicationContext()).thenReturn(mockContext);
@@ -38,7 +43,19 @@ public void getInstanceManagerAfterOnAttachedToEngine() {
3843

3944
webViewFlutterPlugin.onAttachedToEngine(mockPluginBinding);
4045

41-
assertNotNull(webViewFlutterPlugin.getInstanceManager());
46+
final InstanceManager instanceManager = webViewFlutterPlugin.getInstanceManager();
47+
assertNotNull(instanceManager);
48+
49+
final WebView mockWebView = mock(WebView.class);
50+
instanceManager.addDartCreatedInstance(mockWebView, 0);
51+
52+
final PluginRegistry mockPluginRegistry = mock(PluginRegistry.class);
53+
when(mockPluginRegistry.get(WebViewFlutterPlugin.class)).thenReturn(webViewFlutterPlugin);
54+
55+
final FlutterEngine mockFlutterEngine = mock(FlutterEngine.class);
56+
when(mockFlutterEngine.getPlugins()).thenReturn(mockPluginRegistry);
57+
58+
assertEquals(WebViewFlutterAndroidExternalApi.getWebView(mockFlutterEngine, 0), mockWebView);
4259

4360
webViewFlutterPlugin.onDetachedFromEngine(mockPluginBinding);
4461
}

packages/webview_flutter/webview_flutter_android/lib/src/android_webview_controller.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,16 @@ class AndroidWebViewController extends PlatformWebViewController {
144144
return webViewProxy.setWebContentsDebuggingEnabled(enabled);
145145
}
146146

147+
/// Identifier used to retrieve the underlying native `WKWebView`.
148+
///
149+
/// This is typically used by other plugins to retrieve the native `WebView`
150+
/// from an `InstanceManager`.
151+
///
152+
/// See Java method `WebViewFlutterPlugin.getWebView`.
153+
int get webViewIdentifier =>
154+
// ignore: invalid_use_of_visible_for_testing_member
155+
android_webview.WebView.api.instanceManager.getIdentifier(_webView)!;
156+
147157
@override
148158
Future<void> loadFile(
149159
String absoluteFilePath,

packages/webview_flutter/webview_flutter_android/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: webview_flutter_android
22
description: A Flutter plugin that provides a WebView widget on Android.
33
repository: https://github.com/flutter/plugins/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: 3.2.4
5+
version: 3.3.0
66

77
environment:
88
sdk: ">=2.17.0 <3.0.0"

packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.dart

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import 'package:mockito/mockito.dart';
1414
import 'package:webview_flutter_android/src/android_proxy.dart';
1515
import 'package:webview_flutter_android/src/android_webview.dart'
1616
as android_webview;
17+
import 'package:webview_flutter_android/src/android_webview_api_impls.dart';
1718
import 'package:webview_flutter_android/src/instance_manager.dart';
1819
import 'package:webview_flutter_android/src/platform_views_service_proxy.dart';
1920
import 'package:webview_flutter_android/webview_flutter_android.dart';
@@ -884,6 +885,29 @@ void main() {
884885
verify(mockSettings.setMediaPlaybackRequiresUserGesture(true)).called(1);
885886
});
886887

888+
test('webViewIdentifier', () {
889+
final MockWebView mockWebView = MockWebView();
890+
final InstanceManager instanceManager = InstanceManager(
891+
onWeakReferenceRemoved: (_) {},
892+
);
893+
instanceManager.addHostCreatedInstance(mockWebView, 0);
894+
895+
android_webview.WebView.api = WebViewHostApiImpl(
896+
instanceManager: instanceManager,
897+
);
898+
899+
final AndroidWebViewController controller = createControllerWithMocks(
900+
mockWebView: mockWebView,
901+
);
902+
903+
expect(
904+
controller.webViewIdentifier,
905+
0,
906+
);
907+
908+
android_webview.WebView.api = WebViewHostApiImpl();
909+
});
910+
887911
group('AndroidWebViewWidget', () {
888912
testWidgets('Builds Android view using supplied parameters',
889913
(WidgetTester tester) async {

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.1.0
2+
3+
* Adds support to access native `WKWebView`.
4+
15
## 3.0.5
26

37
* Renames Pigeon output files.

packages/webview_flutter/webview_flutter_wkwebview/README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,24 @@ The Apple WKWebView implementation of [`webview_flutter`][1].
77
This package is [endorsed][2], which means you can simply use `webview_flutter`
88
normally. This package will be automatically included in your app when you do.
99

10+
### External Native API
11+
12+
The plugin also provides a native API accessible by the native code of iOS applications or packages.
13+
This API follows the convention of breaking changes of the Dart API, which means that any changes to
14+
the class that are not backwards compatible will only be made with a major version change of the
15+
plugin. Native code other than this external API does not follow breaking change conventions, so
16+
app or plugin clients should not use any other native APIs.
17+
18+
The API can be accessed by importing the native plugin `webview_flutter_wkwebview`:
19+
20+
Objective-C:
21+
22+
```objectivec
23+
@import webview_flutter_wkwebview;
24+
```
25+
26+
Then you will have access to the native class `FWFWebViewFlutterWKWebViewExternalAPI`.
27+
1028
## Contributing
1129

1230
This package uses [pigeon][3] to generate the communication layer between Flutter and the host

0 commit comments

Comments
 (0)