Thanks to visit codestin.com
Credit goes to github.com

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

[webview_flutter_android][webview_flutter_wkwebview] Adds support to retrieve native WebView #7071

Merged
merged 14 commits into from
Feb 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 3.3.0

* Adds support to access native `WebView`.

## 3.2.4

* Renames Pigeon output files.
Expand Down
16 changes: 16 additions & 0 deletions packages/webview_flutter/webview_flutter_android/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,22 @@ This can be configured for versions >=23 with
`AndroidWebViewWidgetCreationParams.displayWithHybridComposition`. See https://pub.dev/packages/webview_flutter#platform-specific-features
for more details on setting platform-specific features in the main plugin.

### External Native API

The plugin also provides a native API accessible by the native code of Android applications or
packages. This API follows the convention of breaking changes of the Dart API, which means that any
changes to the class that are not backwards compatible will only be made with a major version change
of the plugin. Native code other than this external API does not follow breaking change conventions,
so app or plugin clients should not use any other native APIs.

The API can be accessed by importing the native class `WebViewFlutterAndroidExternalApi`:

Java:

```java
import io.flutter.plugins.webviewflutter.WebViewFlutterAndroidExternalApi;
```

## Contributing

This package uses [pigeon][3] to generate the communication layer between Flutter and the host
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugins.webviewflutter;

import android.webkit.WebView;
import androidx.annotation.Nullable;
import io.flutter.embedding.engine.FlutterEngine;

/**
* App and package facing native API provided by the `webview_flutter_android` plugin.
*
* <p>This class follows the convention of breaking changes of the Dart API, which means that any
* changes to the class that are not backwards compatible will only be made with a major version
* change of the plugin.
*
* <p>Native code other than this external API does not follow breaking change conventions, so app
* or plugin clients should not use any other native APIs.
*/
@SuppressWarnings("unused")
public interface WebViewFlutterAndroidExternalApi {
/**
* Retrieves the {@link WebView} that is associated with `identifier`.
*
* <p>See the Dart method `AndroidWebViewController.webViewIdentifier` to get the identifier of an
* underlying `WebView`.
*
* @param engine the execution environment the {@link WebViewFlutterPlugin} should belong to. If
* the engine doesn't contain an attached instance of {@link WebViewFlutterPlugin}, this
* method returns null.
* @param identifier the associated identifier of the `WebView`.
* @return the `WebView` associated with `identifier` or null if a `WebView` instance associated
* with `identifier` could not be found.
*/
@Nullable
static WebView getWebView(FlutterEngine engine, long identifier) {
final WebViewFlutterPlugin webViewPlugin =
(WebViewFlutterPlugin) engine.getPlugins().get(WebViewFlutterPlugin.class);

if (webViewPlugin != null && webViewPlugin.getInstanceManager() != null) {
final Object instance = webViewPlugin.getInstanceManager().getInstance(identifier);
if (instance instanceof WebView) {
return (WebView) instance;
}
}

return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
* <p>Call {@link #registerWith} to use the stable {@code io.flutter.plugin.common} package instead.
*/
public class WebViewFlutterPlugin implements FlutterPlugin, ActivityAware {
private InstanceManager instanceManager;
@Nullable private InstanceManager instanceManager;

private FlutterPluginBinding pluginBinding;
private WebViewHostApiImpl webViewHostApi;
Expand Down Expand Up @@ -148,7 +148,10 @@ public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {

@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
instanceManager.close();
if (instanceManager != null) {
instanceManager.close();
instanceManager = null;
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@

package io.flutter.plugins.webviewflutter;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import android.content.Context;
import android.webkit.WebView;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.PluginRegistry;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.platform.PlatformViewRegistry;
import org.junit.Rule;
Expand All @@ -17,7 +22,7 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

public class WebViewFlutterPluginTest {
public class WebViewFlutterAndroidExternalApiTest {
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();

@Mock Context mockContext;
Expand All @@ -29,7 +34,7 @@ public class WebViewFlutterPluginTest {
@Mock FlutterPlugin.FlutterPluginBinding mockPluginBinding;

@Test
public void getInstanceManagerAfterOnAttachedToEngine() {
public void getWebView() {
final WebViewFlutterPlugin webViewFlutterPlugin = new WebViewFlutterPlugin();

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

webViewFlutterPlugin.onAttachedToEngine(mockPluginBinding);

assertNotNull(webViewFlutterPlugin.getInstanceManager());
final InstanceManager instanceManager = webViewFlutterPlugin.getInstanceManager();
assertNotNull(instanceManager);

final WebView mockWebView = mock(WebView.class);
instanceManager.addDartCreatedInstance(mockWebView, 0);

final PluginRegistry mockPluginRegistry = mock(PluginRegistry.class);
when(mockPluginRegistry.get(WebViewFlutterPlugin.class)).thenReturn(webViewFlutterPlugin);

final FlutterEngine mockFlutterEngine = mock(FlutterEngine.class);
when(mockFlutterEngine.getPlugins()).thenReturn(mockPluginRegistry);

assertEquals(WebViewFlutterAndroidExternalApi.getWebView(mockFlutterEngine, 0), mockWebView);

webViewFlutterPlugin.onDetachedFromEngine(mockPluginBinding);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,16 @@ class AndroidWebViewController extends PlatformWebViewController {
return webViewProxy.setWebContentsDebuggingEnabled(enabled);
}

/// Identifier used to retrieve the underlying native `WKWebView`.
///
/// This is typically used by other plugins to retrieve the native `WebView`
/// from an `InstanceManager`.
///
/// See Java method `WebViewFlutterPlugin.getWebView`.
int get webViewIdentifier =>
// ignore: invalid_use_of_visible_for_testing_member
android_webview.WebView.api.instanceManager.getIdentifier(_webView)!;

@override
Future<void> loadFile(
String absoluteFilePath,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: webview_flutter_android
description: A Flutter plugin that provides a WebView widget on Android.
repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_android
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22
version: 3.2.4
version: 3.3.0

environment:
sdk: ">=2.17.0 <3.0.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import 'package:mockito/mockito.dart';
import 'package:webview_flutter_android/src/android_proxy.dart';
import 'package:webview_flutter_android/src/android_webview.dart'
as android_webview;
import 'package:webview_flutter_android/src/android_webview_api_impls.dart';
import 'package:webview_flutter_android/src/instance_manager.dart';
import 'package:webview_flutter_android/src/platform_views_service_proxy.dart';
import 'package:webview_flutter_android/webview_flutter_android.dart';
Expand Down Expand Up @@ -884,6 +885,29 @@ void main() {
verify(mockSettings.setMediaPlaybackRequiresUserGesture(true)).called(1);
});

test('webViewIdentifier', () {
final MockWebView mockWebView = MockWebView();
final InstanceManager instanceManager = InstanceManager(
onWeakReferenceRemoved: (_) {},
);
instanceManager.addHostCreatedInstance(mockWebView, 0);

android_webview.WebView.api = WebViewHostApiImpl(
instanceManager: instanceManager,
);

final AndroidWebViewController controller = createControllerWithMocks(
mockWebView: mockWebView,
);

expect(
controller.webViewIdentifier,
0,
);

android_webview.WebView.api = WebViewHostApiImpl();
});

group('AndroidWebViewWidget', () {
testWidgets('Builds Android view using supplied parameters',
(WidgetTester tester) async {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 3.1.0

* Adds support to access native `WKWebView`.

## 3.0.5

* Renames Pigeon output files.
Expand Down
18 changes: 18 additions & 0 deletions packages/webview_flutter/webview_flutter_wkwebview/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,24 @@ The Apple WKWebView implementation of [`webview_flutter`][1].
This package is [endorsed][2], which means you can simply use `webview_flutter`
normally. This package will be automatically included in your app when you do.

### External Native API

The plugin also provides a native API accessible by the native code of iOS applications or packages.
This API follows the convention of breaking changes of the Dart API, which means that any changes to
the class that are not backwards compatible will only be made with a major version change of the
plugin. Native code other than this external API does not follow breaking change conventions, so
app or plugin clients should not use any other native APIs.

The API can be accessed by importing the native plugin `webview_flutter_wkwebview`:

Objective-C:

```objectivec
@import webview_flutter_wkwebview;
```

Then you will have access to the native class `FWFWebViewFlutterWKWebViewExternalAPI`.

## Contributing

This package uses [pigeon][3] to generate the communication layer between Flutter and the host
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objectVersion = 54;
objects = {

/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
8F4FF949299ADC2D000A6586 /* FWFWebViewFlutterWKWebViewExternalAPITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F4FF948299ADC2D000A6586 /* FWFWebViewFlutterWKWebViewExternalAPITests.m */; };
8FA6A87928062CD000A4B183 /* FWFInstanceManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */; };
8FB79B5328134C3100C101D3 /* FWFWebViewHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B5228134C3100C101D3 /* FWFWebViewHostApiTests.m */; };
8FB79B55281B24F600C101D3 /* FWFDataConvertersTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B54281B24F600C101D3 /* FWFDataConvertersTests.m */; };
Expand Down Expand Up @@ -76,6 +77,7 @@
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
8F4FF948299ADC2D000A6586 /* FWFWebViewFlutterWKWebViewExternalAPITests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FWFWebViewFlutterWKWebViewExternalAPITests.m; sourceTree = "<group>"; };
8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFInstanceManagerTests.m; sourceTree = "<group>"; };
8FB79B5228134C3100C101D3 /* FWFWebViewHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFWebViewHostApiTests.m; sourceTree = "<group>"; };
8FB79B54281B24F600C101D3 /* FWFDataConvertersTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFDataConvertersTests.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -145,6 +147,7 @@
isa = PBXGroup;
children = (
68BDCAED23C3F7CB00D9C032 /* Info.plist */,
8F4FF948299ADC2D000A6586 /* FWFWebViewFlutterWKWebViewExternalAPITests.m */,
8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */,
8FB79B5228134C3100C101D3 /* FWFWebViewHostApiTests.m */,
8FB79B54281B24F600C101D3 /* FWFDataConvertersTests.m */,
Expand Down Expand Up @@ -379,10 +382,12 @@
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
Expand Down Expand Up @@ -415,6 +420,7 @@
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
Expand Down Expand Up @@ -463,6 +469,7 @@
8FB79B5328134C3100C101D3 /* FWFWebViewHostApiTests.m in Sources */,
8FB79B73282096B500C101D3 /* FWFScriptMessageHandlerHostApiTests.m in Sources */,
8FB79B7928209D1300C101D3 /* FWFUserContentControllerHostApiTests.m in Sources */,
8F4FF949299ADC2D000A6586 /* FWFWebViewFlutterWKWebViewExternalAPITests.m in Sources */,
8FB79B6B28204EE500C101D3 /* FWFWebsiteDataStoreHostApiTests.m in Sources */,
8FB79B8F2820BAB300C101D3 /* FWFScrollViewHostApiTests.m in Sources */,
8FB79B912820BAC700C101D3 /* FWFUIViewHostApiTests.m in Sources */,
Expand Down Expand Up @@ -533,7 +540,11 @@
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
INFOPLIST_FILE = RunnerTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner";
Expand All @@ -547,7 +558,11 @@
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
INFOPLIST_FILE = RunnerTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner";
Expand Down Expand Up @@ -672,7 +687,10 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
Expand All @@ -695,7 +713,10 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
Expand All @@ -711,7 +732,11 @@
buildSettings = {
CODE_SIGN_STYLE = Automatic;
INFOPLIST_FILE = RunnerUITests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
Expand All @@ -724,7 +749,11 @@
buildSettings = {
CODE_SIGN_STYLE = Automatic;
INFOPLIST_FILE = RunnerUITests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
Expand Down
Loading