Added initialSize property and setSize/getSize methods to the HeadlessInAppWebView class, androidOnScaleChanged event is deprecated - use onZoomScaleChanged event, getScale method is deprecated - use getZoomScale method, Removed final keyword for all HeadlessInAppWebView events, Fixed wrong usage of Android WebView scale property

This commit is contained in:
Lorenzo Pichilli 2021-03-26 21:04:44 +01:00
parent 14d06fad48
commit 22ea0091cd
29 changed files with 712 additions and 709 deletions

View File

@ -1,420 +0,0 @@
<component name="libraryTable">
<library name="Dart Packages" type="DartPackagesLibraryType">
<properties>
<option name="packageNameToDirsMap">
<entry key="archive">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/archive-3.0.0/lib" />
</list>
</value>
</entry>
<entry key="async">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/async-2.5.0/lib" />
</list>
</value>
</entry>
<entry key="boolean_selector">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/lib" />
</list>
</value>
</entry>
<entry key="characters">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/characters-1.1.0/lib" />
</list>
</value>
</entry>
<entry key="charcode">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/charcode-1.2.0/lib" />
</list>
</value>
</entry>
<entry key="clock">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0/lib" />
</list>
</value>
</entry>
<entry key="collection">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/collection-1.15.0/lib" />
</list>
</value>
</entry>
<entry key="crypto">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/crypto-3.0.0/lib" />
</list>
</value>
</entry>
<entry key="cupertino_icons">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/cupertino_icons-1.0.2/lib" />
</list>
</value>
</entry>
<entry key="fake_async">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/fake_async-1.2.0/lib" />
</list>
</value>
</entry>
<entry key="ffi">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/ffi-1.0.0/lib" />
</list>
</value>
</entry>
<entry key="file">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/file-6.1.0/lib" />
</list>
</value>
</entry>
<entry key="flutter">
<value>
<list>
<option value="$USER_HOME$/fvm/versions/2.1.0-10.0.pre/packages/flutter/lib" />
</list>
</value>
</entry>
<entry key="flutter_downloader">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/lib" />
</list>
</value>
</entry>
<entry key="flutter_driver">
<value>
<list>
<option value="$USER_HOME$/fvm/versions/2.1.0-10.0.pre/packages/flutter_driver/lib" />
</list>
</value>
</entry>
<entry key="flutter_test">
<value>
<list>
<option value="$USER_HOME$/fvm/versions/2.1.0-10.0.pre/packages/flutter_test/lib" />
</list>
</value>
</entry>
<entry key="fuchsia_remote_debug_protocol">
<value>
<list>
<option value="$USER_HOME$/fvm/versions/2.1.0-10.0.pre/packages/fuchsia_remote_debug_protocol/lib" />
</list>
</value>
</entry>
<entry key="integration_test">
<value>
<list>
<option value="$USER_HOME$/fvm/versions/2.1.0-10.0.pre/packages/integration_test/lib" />
</list>
</value>
</entry>
<entry key="matcher">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.10/lib" />
</list>
</value>
</entry>
<entry key="meta">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/meta-1.3.0/lib" />
</list>
</value>
</entry>
<entry key="path">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path-1.8.0/lib" />
</list>
</value>
</entry>
<entry key="path_provider">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.0-nullsafety/lib" />
</list>
</value>
</entry>
<entry key="path_provider_linux">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-0.2.0-nullsafety/lib" />
</list>
</value>
</entry>
<entry key="path_provider_macos">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.5-nullsafety/lib" />
</list>
</value>
</entry>
<entry key="path_provider_platform_interface">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path_provider_platform_interface-2.0.0-nullsafety/lib" />
</list>
</value>
</entry>
<entry key="path_provider_windows">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-0.1.0-nullsafety.3/lib" />
</list>
</value>
</entry>
<entry key="pedantic">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/pedantic-1.10.0/lib" />
</list>
</value>
</entry>
<entry key="permission_handler">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.1.0+2/lib" />
</list>
</value>
</entry>
<entry key="permission_handler_platform_interface">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/permission_handler_platform_interface-2.0.2/lib" />
</list>
</value>
</entry>
<entry key="platform">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/platform-3.0.0/lib" />
</list>
</value>
</entry>
<entry key="plugin_platform_interface">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/plugin_platform_interface-1.1.0-nullsafety.1/lib" />
</list>
</value>
</entry>
<entry key="process">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/process-4.1.0/lib" />
</list>
</value>
</entry>
<entry key="sky_engine">
<value>
<list>
<option value="$USER_HOME$/fvm/versions/2.1.0-10.0.pre/bin/cache/pkg/sky_engine/lib" />
</list>
</value>
</entry>
<entry key="source_span">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.1/lib" />
</list>
</value>
</entry>
<entry key="stack_trace">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib" />
</list>
</value>
</entry>
<entry key="stream_channel">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0/lib" />
</list>
</value>
</entry>
<entry key="string_scanner">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0/lib" />
</list>
</value>
</entry>
<entry key="sync_http">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/sync_http-0.3.0/lib" />
</list>
</value>
</entry>
<entry key="term_glyph">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0/lib" />
</list>
</value>
</entry>
<entry key="test_api">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/test_api-0.2.19/lib" />
</list>
</value>
</entry>
<entry key="typed_data">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.0/lib" />
</list>
</value>
</entry>
<entry key="url_launcher">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.6/lib" />
</list>
</value>
</entry>
<entry key="url_launcher_linux">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/url_launcher_linux-0.1.0-nullsafety.3/lib" />
</list>
</value>
</entry>
<entry key="url_launcher_macos">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-0.1.0-nullsafety.2/lib" />
</list>
</value>
</entry>
<entry key="url_launcher_platform_interface">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/url_launcher_platform_interface-2.0.0-nullsafety.1/lib" />
</list>
</value>
</entry>
<entry key="url_launcher_windows">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-0.1.0-nullsafety.2/lib" />
</list>
</value>
</entry>
<entry key="vector_math">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.0/lib" />
</list>
</value>
</entry>
<entry key="vm_service">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/vm_service-6.0.1-nullsafety.1/lib" />
</list>
</value>
</entry>
<entry key="webdriver">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/webdriver-3.0.0/lib" />
</list>
</value>
</entry>
<entry key="win32">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/win32-2.0.0/lib" />
</list>
</value>
</entry>
<entry key="xdg_directories">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/xdg_directories-0.2.0-nullsafety.1/lib" />
</list>
</value>
</entry>
</option>
</properties>
<CLASSES>
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/archive-3.0.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/async-2.5.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/characters-1.1.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/charcode-1.2.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/collection-1.15.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/crypto-3.0.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/cupertino_icons-1.0.2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/fake_async-1.2.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/ffi-1.0.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/file-6.1.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.10/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/meta-1.3.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path-1.8.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.0-nullsafety/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-0.2.0-nullsafety/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.5-nullsafety/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path_provider_platform_interface-2.0.0-nullsafety/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-0.1.0-nullsafety.3/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/pedantic-1.10.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.1.0+2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/permission_handler_platform_interface-2.0.2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/platform-3.0.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/plugin_platform_interface-1.1.0-nullsafety.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/process-4.1.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/sync_http-0.3.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/test_api-0.2.19/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.6/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/url_launcher_linux-0.1.0-nullsafety.3/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-0.1.0-nullsafety.2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/url_launcher_platform_interface-2.0.0-nullsafety.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-0.1.0-nullsafety.2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/vm_service-6.0.1-nullsafety.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/webdriver-3.0.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/win32-2.0.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/xdg_directories-0.2.0-nullsafety.1/lib" />
<root url="file://$USER_HOME$/fvm/versions/2.1.0-10.0.pre/bin/cache/pkg/sky_engine/lib" />
<root url="file://$USER_HOME$/fvm/versions/2.1.0-10.0.pre/packages/flutter/lib" />
<root url="file://$USER_HOME$/fvm/versions/2.1.0-10.0.pre/packages/flutter_driver/lib" />
<root url="file://$USER_HOME$/fvm/versions/2.1.0-10.0.pre/packages/flutter_test/lib" />
<root url="file://$USER_HOME$/fvm/versions/2.1.0-10.0.pre/packages/fuchsia_remote_debug_protocol/lib" />
<root url="file://$USER_HOME$/fvm/versions/2.1.0-10.0.pre/packages/integration_test/lib" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

View File

@ -1,6 +1,8 @@
<component name="libraryTable">
<library name="Flutter Plugins">
<CLASSES />
<CLASSES>
<root url="file://$PROJECT_DIR$" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>

View File

@ -1,3 +1,12 @@
## 5.3.0
- Added `initialSize` property to the `HeadlessInAppWebView` class
- Added `setSize` and `getSize` methods to the `HeadlessInAppWebView` class
- `androidOnScaleChanged` WebView event is now deprecated. Use the new `onZoomScaleChanged` WebView event, that is available for both Android and iOS
- `getScale` WebView method is now deprecated. Use the new `getZoomScale` WebView method
- Removed `final` keyword for all `HeadlessInAppWebView` events
- Fixed wrong usage of Android WebView scale property
## 5.2.1+1
- Fixed iOS "Unexpectedly found nil while unwrapping an Optional value: file flutter_inappwebview/WKUserContentController.swift, line 36" error when `applePayAPIEnabled` iOS-specific WebView option is enabled

View File

@ -10,7 +10,7 @@ import com.pichillilorenzo.flutter_inappwebview.chrome_custom_tabs.ChromeSafariB
import com.pichillilorenzo.flutter_inappwebview.credential_database.CredentialDatabaseHandler;
import com.pichillilorenzo.flutter_inappwebview.in_app_browser.InAppBrowserManager;
import com.pichillilorenzo.flutter_inappwebview.in_app_webview.FlutterWebViewFactory;
import com.pichillilorenzo.flutter_inappwebview.in_app_webview.HeadlessInAppWebViewManager;
import com.pichillilorenzo.flutter_inappwebview.headless_in_app_webview.HeadlessInAppWebViewManager;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;

View File

@ -2,7 +2,6 @@ package com.pichillilorenzo.flutter_inappwebview;
import android.net.Uri;
import android.os.Build;
import android.util.Log;
import android.webkit.ValueCallback;
import android.webkit.WebView;
@ -332,8 +331,8 @@ public class InAppWebViewMethodHandler implements MethodChannel.MethodCallHandle
case "getOriginalUrl":
result.success((webView != null) ? webView.getOriginalUrl() : null);
break;
case "getScale":
result.success((webView != null) ? webView.getUpdatedScale() : null);
case "getZoomScale":
result.success((webView != null) ? webView.zoomScale : null);
break;
case "getSelectedText":
if (webView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {

View File

@ -1,5 +1,6 @@
package com.pichillilorenzo.flutter_inappwebview;
import android.content.Context;
import android.content.res.AssetManager;
import android.net.http.SslCertificate;
import android.os.Build;
@ -287,4 +288,8 @@ public class Util {
} while (i < newline);
}
}
public static float getPixelDensity(Context context) {
return context.getResources().getDisplayMetrics().density;
}
}

View File

@ -0,0 +1,119 @@
package com.pichillilorenzo.flutter_inappwebview.headless_in_app_webview;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.pichillilorenzo.flutter_inappwebview.Shared;
import com.pichillilorenzo.flutter_inappwebview.Util;
import com.pichillilorenzo.flutter_inappwebview.in_app_webview.FlutterWebView;
import com.pichillilorenzo.flutter_inappwebview.types.Size2D;
import java.util.HashMap;
import java.util.Map;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
public class HeadlessInAppWebView implements MethodChannel.MethodCallHandler {
protected static final String LOG_TAG = "HeadlessInAppWebView";
@NonNull
public final String id;
public final MethodChannel channel;
@Nullable
public FlutterWebView flutterWebView;
public HeadlessInAppWebView(BinaryMessenger messenger, @NonNull String id, @NonNull FlutterWebView flutterWebView) {
this.id = id;
this.flutterWebView = flutterWebView;
this.channel = new MethodChannel(messenger, "com.pichillilorenzo/flutter_headless_inappwebview_" + id);
channel.setMethodCallHandler(this);
}
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
switch (call.method) {
case "dispose":
dispose();
result.success(true);
break;
case "setSize":
{
Map<String, Object> sizeMap = (Map<String, Object>) call.argument("size");
Size2D size = Size2D.fromMap(sizeMap);
if (size != null)
setSize(size);
}
result.success(true);
break;
case "getSize":
{
Size2D size = getSize();
result.success(size != null ? size.toMap() : null);
}
break;
default:
result.notImplemented();
}
}
public void onWebViewCreated() {
Map<String, Object> obj = new HashMap<>();
channel.invokeMethod("onWebViewCreated", obj);
}
public void prepare(Map<String, Object> params) {
// Add the headless WebView to the view hierarchy.
// This way is also possible to take screenshots.
ViewGroup contentView = (ViewGroup) Shared.activity.findViewById(android.R.id.content);
ViewGroup mainView = (ViewGroup) (contentView).getChildAt(0);
if (mainView != null) {
View view = flutterWebView.getView();
final Map<String, Object> initialSize = (Map<String, Object>) params.get("initialSize");
Size2D size = Size2D.fromMap(initialSize);
if (size != null) {
setSize(size);
} else {
view.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
}
mainView.addView(view, 0);
view.setVisibility(View.INVISIBLE);
}
}
public void setSize(@NonNull Size2D size) {
if (flutterWebView != null && flutterWebView.webView != null) {
View view = flutterWebView.getView();
float scale = Util.getPixelDensity(view.getContext());
view.setLayoutParams(new FrameLayout.LayoutParams((int) (size.getWidth() * scale), (int) (size.getHeight() * scale)));
}
}
@Nullable
public Size2D getSize() {
if (flutterWebView != null && flutterWebView.webView != null) {
View view = flutterWebView.getView();
float scale = Util.getPixelDensity(view.getContext());
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
return new Size2D(layoutParams.width / scale, layoutParams.height / scale);
}
return null;
}
public void dispose() {
channel.setMethodCallHandler(null);
HeadlessInAppWebViewManager.webViews.remove(id);
ViewGroup contentView = (ViewGroup) Shared.activity.findViewById(android.R.id.content);
ViewGroup mainView = (ViewGroup) (contentView).getChildAt(0);
if (mainView != null) {
mainView.removeView(flutterWebView.getView());
}
flutterWebView.dispose();
flutterWebView = null;
}
}

View File

@ -19,11 +19,10 @@
*
*/
package com.pichillilorenzo.flutter_inappwebview.in_app_webview;
import android.app.Activity;
package com.pichillilorenzo.flutter_inappwebview.headless_in_app_webview;
import com.pichillilorenzo.flutter_inappwebview.Shared;
import com.pichillilorenzo.flutter_inappwebview.in_app_webview.FlutterWebView;
import java.util.HashMap;
import java.util.Map;
@ -33,14 +32,11 @@ import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.Result;
/**
* InAppBrowserManager
*/
public class HeadlessInAppWebViewManager implements MethodChannel.MethodCallHandler {
public MethodChannel channel;
protected static final String LOG_TAG = "HeadlessInAppWebViewManager";
Map<String, FlutterWebView> flutterWebViews = new HashMap<>();
public MethodChannel channel;
public static final Map<String, HeadlessInAppWebView> webViews = new HashMap<>();
public HeadlessInAppWebViewManager(BinaryMessenger messenger) {
channel = new MethodChannel(messenger, "com.pichillilorenzo/flutter_headless_inappwebview");
@ -49,40 +45,34 @@ public class HeadlessInAppWebViewManager implements MethodChannel.MethodCallHand
@Override
public void onMethodCall(final MethodCall call, final Result result) {
final Activity activity = Shared.activity;
final String id = (String) call.argument("id");
switch (call.method) {
case "createHeadlessWebView":
case "run":
{
HashMap<String, Object> params = (HashMap<String, Object>) call.argument("params");
createHeadlessWebView(activity, id, params);
HeadlessInAppWebViewManager.run(id, params);
}
result.success(true);
break;
case "disposeHeadlessWebView":
disposeHeadlessWebView(id);
result.success(true);
break;
default:
result.notImplemented();
}
}
public void createHeadlessWebView(Activity activity, String id, HashMap<String, Object> params) {
FlutterWebView flutterWebView = new FlutterWebView(Shared.messenger, activity, id, params, null);
flutterWebViews.put(id, flutterWebView);
}
public void disposeHeadlessWebView(String id) {
if (flutterWebViews.containsKey(id)) {
flutterWebViews.get(id).dispose();
flutterWebViews.remove(id);
}
public static void run(String id, HashMap<String, Object> params) {
FlutterWebView flutterWebView = new FlutterWebView(Shared.messenger, Shared.activity, id, params, null);
HeadlessInAppWebView headlessInAppWebView = new HeadlessInAppWebView(Shared.messenger, id, flutterWebView);
HeadlessInAppWebViewManager.webViews.put(id, headlessInAppWebView);
headlessInAppWebView.prepare(params);
headlessInAppWebView.onWebViewCreated();
flutterWebView.makeInitialLoad(params);
}
public void dispose() {
HeadlessInAppWebViewManager.webViews.clear();
channel.setMethodCallHandler(null);
}
}

View File

@ -44,16 +44,14 @@ public class FlutterWebView implements PlatformView {
public InAppWebViewMethodHandler methodCallDelegate;
public PullToRefreshLayout pullToRefreshLayout;
public FlutterWebView(BinaryMessenger messenger, final Context context, Object id, HashMap<String, Object> params, View containerView) {
public FlutterWebView(BinaryMessenger messenger, final Context context, Object id,
HashMap<String, Object> params, View containerView) {
channel = new MethodChannel(messenger, "com.pichillilorenzo/flutter_inappwebview_" + id);
DisplayListenerProxy displayListenerProxy = new DisplayListenerProxy();
DisplayManager displayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
displayListenerProxy.onPreWebViewInitialization(displayManager);
Map<String, Object> initialUrlRequest = (Map<String, Object>) params.get("initialUrlRequest");
final String initialFile = (String) params.get("initialFile");
final Map<String, String> initialData = (Map<String, String>) params.get("initialData");
Map<String, Object> initialOptions = (Map<String, Object>) params.get("initialOptions");
Map<String, Object> contextMenu = (Map<String, Object>) params.get("contextMenu");
Integer windowId = (Integer) params.get("windowId");
@ -95,6 +93,18 @@ public class FlutterWebView implements PlatformView {
channel.setMethodCallHandler(methodCallDelegate);
webView.prepare();
}
@Override
public View getView() {
return pullToRefreshLayout != null ? pullToRefreshLayout : webView;
}
public void makeInitialLoad(HashMap<String, Object> params) {
Integer windowId = (Integer) params.get("windowId");
Map<String, Object> initialUrlRequest = (Map<String, Object>) params.get("initialUrlRequest");
final String initialFile = (String) params.get("initialFile");
final Map<String, String> initialData = (Map<String, String>) params.get("initialData");
if (windowId != null) {
Message resultMsg = InAppWebViewChromeClient.windowWebViewMessages.get(windowId);
@ -109,7 +119,6 @@ public class FlutterWebView implements PlatformView {
} catch (IOException e) {
e.printStackTrace();
Log.e(LOG_TAG, initialFile + " asset file cannot be found!", e);
return;
}
}
else if (initialData != null) {
@ -125,16 +134,6 @@ public class FlutterWebView implements PlatformView {
webView.loadUrl(urlRequest);
}
}
if (containerView == null && id instanceof String) {
Map<String, Object> obj = new HashMap<>();
channel.invokeMethod("onHeadlessWebViewCreated", obj);
}
}
@Override
public View getView() {
return pullToRefreshLayout != null ? pullToRefreshLayout : webView;
}
@Override

View File

@ -23,7 +23,9 @@ public class FlutterWebViewFactory extends PlatformViewFactory {
@Override
public PlatformView create(Context context, int id, Object args) {
HashMap<String, Object> params = (HashMap<String, Object>) args;
return new FlutterWebView(messenger, context, id, params, containerView);
FlutterWebView flutterWebView = new FlutterWebView(messenger, context, id, params, containerView);
flutterWebView.makeInitialLoad(params);
return flutterWebView;
}
}

View File

@ -116,7 +116,7 @@ final public class InAppWebView extends InputAwareWebView {
public InAppWebViewOptions options;
public boolean isLoading = false;
public OkHttpClient httpClient;
public float scale = getResources().getDisplayMetrics().density;
public float zoomScale = 1.0f;
int okHttpClientCacheSize = 10 * 1024 * 1024; // 10MB
public ContentBlockerHandler contentBlockerHandler = new ContentBlockerHandler();
public Pattern regexToCancelSubFramesLoadingCompiled;
@ -579,6 +579,8 @@ final public class InAppWebView extends InputAwareWebView {
}
public void takeScreenshot(final @Nullable Map<String, Object> screenshotConfiguration, final MethodChannel.Result result) {
final float pixelDensity = Util.getPixelDensity(getContext());
headlessHandler.post(new Runnable() {
@Override
public void run() {
@ -595,10 +597,10 @@ final public class InAppWebView extends InputAwareWebView {
if (screenshotConfiguration != null) {
Map<String, Double> rect = (Map<String, Double>) screenshotConfiguration.get("rect");
if (rect != null) {
int rectX = (int) Math.floor(rect.get("x") * scale + 0.5);
int rectY = (int) Math.floor(rect.get("y") * scale + 0.5);
int rectWidth = Math.min(resized.getWidth(), (int) Math.floor(rect.get("width") * scale + 0.5));
int rectHeight = Math.min(resized.getHeight(), (int) Math.floor(rect.get("height") * scale + 0.5));
int rectX = (int) Math.floor(rect.get("x") * pixelDensity + 0.5);
int rectY = (int) Math.floor(rect.get("y") * pixelDensity + 0.5);
int rectWidth = Math.min(resized.getWidth(), (int) Math.floor(rect.get("width") * pixelDensity + 0.5));
int rectHeight = Math.min(resized.getHeight(), (int) Math.floor(rect.get("height") * pixelDensity + 0.5));
resized = Bitmap.createBitmap(
resized,
rectX,
@ -609,7 +611,7 @@ final public class InAppWebView extends InputAwareWebView {
Double snapshotWidth = (Double) screenshotConfiguration.get("snapshotWidth");
if (snapshotWidth != null) {
int dstWidth = (int) Math.floor(snapshotWidth * scale + 0.5);
int dstWidth = (int) Math.floor(snapshotWidth * pixelDensity + 0.5);
float ratioBitmap = (float) resized.getWidth() / (float) resized.getHeight();
int dstHeight = (int) ((float) dstWidth / ratioBitmap);
resized = Bitmap.createScaledBitmap(resized, dstWidth, dstHeight, true);
@ -1222,10 +1224,6 @@ final public class InAppWebView extends InputAwareWebView {
}
}
public Float getUpdatedScale() {
return scale;
}
@Override
public void onCreateContextMenu(ContextMenu menu) {
super.onCreateContextMenu(menu);
@ -1509,7 +1507,7 @@ final public class InAppWebView extends InputAwareWebView {
if (floatingContextMenu != null) {
if (value != null && !value.equalsIgnoreCase("null")) {
int x = contextMenuPoint.x;
int y = (int) ((Float.parseFloat(value) * scale) + (floatingContextMenu.getHeight() / 3.5));
int y = (int) ((Float.parseFloat(value) * Util.getPixelDensity(getContext())) + (floatingContextMenu.getHeight() / 3.5));
contextMenuPoint.y = y;
onFloatingActionGlobalLayout(x, y);
} else {

View File

@ -23,6 +23,7 @@ import android.webkit.WebViewClient;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.pichillilorenzo.flutter_inappwebview.Shared;
import com.pichillilorenzo.flutter_inappwebview.Util;
import com.pichillilorenzo.flutter_inappwebview.credential_database.CredentialDatabase;
import com.pichillilorenzo.flutter_inappwebview.in_app_browser.InAppBrowserDelegate;
@ -508,12 +509,12 @@ public class InAppWebViewClient extends WebViewClient {
public void onScaleChanged(WebView view, float oldScale, float newScale) {
super.onScaleChanged(view, oldScale, newScale);
final InAppWebView webView = (InAppWebView) view;
webView.scale = newScale;
webView.zoomScale = newScale / Util.getPixelDensity(webView.getContext());
Map<String, Object> obj = new HashMap<>();
obj.put("oldScale", oldScale);
obj.put("newScale", newScale);
channel.invokeMethod("onScaleChanged", obj);
channel.invokeMethod("onZoomScaleChanged", obj);
}
@RequiresApi(api = Build.VERSION_CODES.O_MR1)

View File

@ -0,0 +1,81 @@
package com.pichillilorenzo.flutter_inappwebview.types;
import androidx.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
public class Size2D {
private double width;
private double height;
public Size2D(double width, double height) {
this.width = width;
this.height = height;
}
@Nullable
public static Size2D fromMap(@Nullable Map<String, Object> map) {
if (map == null) {
return null;
}
Double width = (Double) map.get("width");
Double height = (Double) map.get("height");
assert width != null;
assert height != null;
return new Size2D(width, height);
}
public Map<String, Object> toMap() {
Map<String, Object> sizeMap = new HashMap<>();
sizeMap.put("width", width);
sizeMap.put("height", height);
return sizeMap;
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Size2D size = (Size2D) o;
if (Double.compare(size.width, width) != 0) return false;
return Double.compare(size.height, height) == 0;
}
@Override
public int hashCode() {
int result;
long temp;
temp = Double.doubleToLongBits(width);
result = (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(height);
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public String toString() {
return "Size{" +
"width=" + width +
", height=" + height +
'}';
}
}

View File

@ -3247,6 +3247,45 @@ setTimeout(function() {
await expectLater(onTitleChangedCompleter.future, completes);
});
testWidgets('onZoomScaleChanged', (WidgetTester tester) async {
final Completer controllerCompleter = Completer<InAppWebViewController>();
final Completer<void> pageLoaded = Completer<void>();
final Completer<void> onZoomScaleChangedCompleter = Completer<void>();
var listenForScaleChange = false;
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: InAppWebView(
key: GlobalKey(),
initialUrlRequest:
URLRequest(url: Uri.parse('https://github.com/flutter')),
onWebViewCreated: (controller) {
controllerCompleter.complete(controller);
},
onLoadStop: (controller, url) {
pageLoaded.complete();
},
onZoomScaleChanged: (controller, oldScale, newScale) {
if (listenForScaleChange) {
onZoomScaleChangedCompleter.complete();
}
},
),
),
);
final InAppWebViewController controller =
await controllerCompleter.future;
await pageLoaded.future;
listenForScaleChange = true;
await controller.zoomBy(zoomFactor: 2);
await expectLater(onZoomScaleChangedCompleter.future, completes);
});
testWidgets('androidOnPermissionRequest', (WidgetTester tester) async {
final Completer controllerCompleter = Completer<InAppWebViewController>();
final Completer<void> pageLoaded = Completer<void>();
@ -3341,50 +3380,6 @@ setTimeout(function() {
expect(resourceLoaded, containsAll(resourceList));
}, skip: !Platform.isAndroid);
testWidgets('androidOnScaleChanged', (WidgetTester tester) async {
final Completer controllerCompleter = Completer<InAppWebViewController>();
final Completer<void> pageLoaded = Completer<void>();
final Completer<void> onScaleChangedCompleter = Completer<void>();
var listenForScaleChange = false;
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: InAppWebView(
key: GlobalKey(),
initialUrlRequest:
URLRequest(url: Uri.parse('https://flutter.dev/')),
onWebViewCreated: (controller) {
controllerCompleter.complete(controller);
},
onLoadStop: (controller, url) {
pageLoaded.complete();
},
androidOnScaleChanged: (controller, oldScale, newScale) {
if (listenForScaleChange) {
onScaleChangedCompleter.complete();
}
},
),
),
);
final InAppWebViewController controller =
await controllerCompleter.future;
await pageLoaded.future;
listenForScaleChange = true;
await controller.evaluateJavascript(source: """
var meta = document.createElement('meta');
meta.setAttribute('name', 'viewport');
meta.setAttribute('content', 'width=device-width, initial-scale=2.0, maximum-scale=2.0, minimum-scale=2.0, user-scalable=no');
document.getElementsByTagName('head')[0].appendChild(meta);
""");
await expectLater(onScaleChangedCompleter.future, completes);
}, skip: !Platform.isAndroid);
testWidgets('androidOnReceivedIcon', (WidgetTester tester) async {
final Completer controllerCompleter = Completer<InAppWebViewController>();
final Completer<void> pageLoaded = Completer<void>();
@ -4540,7 +4535,7 @@ setTimeout(function() {
controller.zoomBy(zoomFactor: 3.0, iosAnimated: true), completes);
});
testWidgets('getScale', (WidgetTester tester) async {
testWidgets('getZoomScale', (WidgetTester tester) async {
final Completer controllerCompleter = Completer<InAppWebViewController>();
final Completer<void> pageLoaded = Completer<void>();
@ -4565,7 +4560,7 @@ setTimeout(function() {
await controllerCompleter.future;
await pageLoaded.future;
final scale = await controller.getScale();
final scale = await controller.getZoomScale();
expect(scale, isNonZero);
expect(scale, isPositive);
});

View File

@ -80,6 +80,5 @@
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Flutter Plugins" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>

View File

@ -0,0 +1,95 @@
//
// HeadlessInAppWebView.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 26/03/21.
//
import Foundation
public class HeadlessInAppWebView : FlutterMethodCallDelegate {
var id: String
var channel: FlutterMethodChannel?
var flutterWebView: FlutterWebViewController?
public init(id: String, flutterWebView: FlutterWebViewController) {
self.id = id
super.init()
self.flutterWebView = flutterWebView
self.channel = FlutterMethodChannel(name: "com.pichillilorenzo/flutter_headless_inappwebview_" + id,
binaryMessenger: SwiftFlutterPlugin.instance!.registrar!.messenger())
self.channel?.setMethodCallHandler(self.handle)
}
public override func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
let arguments = call.arguments as? NSDictionary
switch call.method {
case "dispose":
dispose()
result(true)
break
case "setSize":
let sizeMap = arguments!["size"] as? [String: Any?]
if let size = Size2D.fromMap(map: sizeMap) {
setSize(size: size)
}
result(true)
break
case "getSize":
result(getSize()?.toMap())
break
default:
result(FlutterMethodNotImplemented)
break
}
}
public func onWebViewCreated() {
let arguments: [String: Any?] = [:]
channel?.invokeMethod("onWebViewCreated", arguments: arguments)
}
public func prepare(params: NSDictionary) {
if let view = flutterWebView?.view() {
view.alpha = 0.01
let initialSize = params["initialSize"] as? [String: Any?]
if let size = Size2D.fromMap(map: initialSize) {
setSize(size: size)
} else {
view.frame = CGRect(x: 0.0, y: 0.0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
}
if let keyWindow = UIApplication.shared.keyWindow {
/// Note: The WKWebView behaves very unreliable when rendering offscreen
/// on a device. This is especially true with JavaScript, which simply
/// won't be executed sometimes.
/// So, add the headless WKWebView to the view hierarchy.
/// This way is also possible to take screenshots.
keyWindow.insertSubview(view, at: 0)
keyWindow.sendSubviewToBack(view)
}
}
}
public func setSize(size: Size2D) {
if let view = flutterWebView?.view() {
let width = size.width == -1.0 ? UIScreen.main.bounds.width : CGFloat(size.width)
let height = size.height == -1.0 ? UIScreen.main.bounds.height : CGFloat(size.height)
view.frame = CGRect(x: 0.0, y: 0.0, width: width, height: height)
}
}
public func getSize() -> Size2D? {
if let view = flutterWebView?.view() {
return Size2D(width: Double(view.frame.width), height: Double(view.frame.height))
}
return nil
}
public func dispose() {
channel?.setMethodCallHandler(nil)
channel = nil
HeadlessInAppWebViewManager.webViews.removeValue(forKey: id)
flutterWebView = nil
}
}

View File

@ -16,7 +16,7 @@ import AVFoundation
public class HeadlessInAppWebViewManager: NSObject, FlutterPlugin {
static var registrar: FlutterPluginRegistrar?
static var channel: FlutterMethodChannel?
var flutterWebViews: [String: FlutterWebViewController] = [:]
static var webViews: [String: HeadlessInAppWebView] = [:]
public static func register(with registrar: FlutterPluginRegistrar) {
@ -34,13 +34,9 @@ public class HeadlessInAppWebViewManager: NSObject, FlutterPlugin {
let id: String = arguments!["id"] as! String
switch call.method {
case "createHeadlessWebView":
case "run":
let params = arguments!["params"] as! [String: Any?]
createHeadlessWebView(id: id, params: params)
result(true)
break
case "disposeHeadlessWebView":
disposeHeadlessWebView(id: id)
HeadlessInAppWebViewManager.run(id: id, params: params)
result(true)
break
default:
@ -49,17 +45,16 @@ public class HeadlessInAppWebViewManager: NSObject, FlutterPlugin {
}
}
public func createHeadlessWebView(id: String, params: [String: Any?]) {
let controller = FlutterWebViewController(registrar: HeadlessInAppWebViewManager.registrar!,
public static func run(id: String, params: [String: Any?]) {
let flutterWebView = FlutterWebViewController(registrar: HeadlessInAppWebViewManager.registrar!,
withFrame: CGRect.zero,
viewIdentifier: id,
arguments: params as NSDictionary)
flutterWebViews[id] = controller
}
public func disposeHeadlessWebView(id: String) {
if let _ = flutterWebViews[id] {
flutterWebViews.removeValue(forKey: id)
}
params: params as NSDictionary)
let headlessInAppWebView = HeadlessInAppWebView(id: id, flutterWebView: flutterWebView)
HeadlessInAppWebViewManager.webViews[id] = headlessInAppWebView
headlessInAppWebView.prepare(params: params as NSDictionary)
headlessInAppWebView.onWebViewCreated()
flutterWebView.makeInitialLoad(params: params as NSDictionary)
}
}

View File

@ -17,7 +17,7 @@ public class FlutterWebViewController: NSObject, FlutterPlatformView {
var myView: UIView?
var methodCallDelegate: InAppWebViewMethodHandler?
init(registrar: FlutterPluginRegistrar, withFrame frame: CGRect, viewIdentifier viewId: Any, arguments args: NSDictionary) {
init(registrar: FlutterPluginRegistrar, withFrame frame: CGRect, viewIdentifier viewId: Any, params: NSDictionary) {
super.init()
self.registrar = registrar
@ -29,14 +29,11 @@ public class FlutterWebViewController: NSObject, FlutterPlatformView {
myView = UIView(frame: frame)
myView!.clipsToBounds = true
let initialUrlRequest = args["initialUrlRequest"] as? [String: Any?]
let initialFile = args["initialFile"] as? String
let initialData = args["initialData"] as? [String: String]
let initialOptions = args["initialOptions"] as! [String: Any?]
let contextMenu = args["contextMenu"] as? [String: Any]
let windowId = args["windowId"] as? Int64
let initialUserScripts = args["initialUserScripts"] as? [[String: Any]]
let pullToRefreshInitialOptions = args["pullToRefreshOptions"] as! [String: Any?]
let initialOptions = params["initialOptions"] as! [String: Any?]
let contextMenu = params["contextMenu"] as? [String: Any]
let windowId = params["windowId"] as? Int64
let initialUserScripts = params["initialUserScripts"] as? [[String: Any]]
let pullToRefreshInitialOptions = params["pullToRefreshOptions"] as! [String: Any?]
var userScripts: [UserScript] = []
if let initialUserScripts = initialUserScripts {
@ -83,6 +80,17 @@ public class FlutterWebViewController: NSObject, FlutterPlatformView {
webView!.options = options
webView!.prepare()
webView!.windowCreated = true
}
public func view() -> UIView {
return myView!
}
public func makeInitialLoad(params: NSDictionary) {
let windowId = params["windowId"] as? Int64
let initialUrlRequest = params["initialUrlRequest"] as? [String: Any?]
let initialFile = params["initialFile"] as? String
let initialData = params["initialData"] as? [String: String]
if windowId == nil {
if #available(iOS 11.0, *) {
@ -116,37 +124,9 @@ public class FlutterWebViewController: NSObject, FlutterPlatformView {
else if let wId = windowId, let webViewTransport = InAppWebView.windowWebViews[wId] {
webView!.load(webViewTransport.request)
}
if (frame.isEmpty && viewId is String) {
/// Note: The WKWebView behaves very unreliable when rendering offscreen
/// on a device. This is especially true with JavaScript, which simply
/// won't be executed sometimes.
/// Therefore, I decided to add this very ugly hack where the rendering
/// webview will be added to the view hierarchy (between the
/// rootViewController's view and the key window).
self.myView!.alpha = 0.01
UIApplication.shared.keyWindow!.insertSubview(self.myView!, at: 0)
let arguments: [String: Any] = ["id": viewId]
channel!.invokeMethod("onHeadlessWebViewCreated", arguments: arguments)
}
}
deinit {
print("FlutterWebViewController - dealloc")
channel?.setMethodCallHandler(nil)
methodCallDelegate?.webView = nil
methodCallDelegate = nil
webView?.dispose()
webView = nil
myView = nil
}
public func view() -> UIView {
return myView!
}
public func load(initialUrlRequest: [String:Any?]?, initialFile: String?, initialData: [String: String]?) {
func load(initialUrlRequest: [String:Any?]?, initialFile: String?, initialData: [String: String]?) {
if let initialFile = initialFile {
do {
try webView?.loadFile(assetFilePath: initialFile)
@ -174,4 +154,18 @@ public class FlutterWebViewController: NSObject, FlutterPlatformView {
webView?.loadUrl(urlRequest: urlRequest, allowingReadAccessTo: allowingReadAccessToURL)
}
}
func dispose() {
channel?.setMethodCallHandler(nil)
methodCallDelegate?.webView = nil
methodCallDelegate = nil
webView?.dispose()
webView = nil
myView = nil
}
deinit {
print("FlutterWebViewController - dealloc")
dispose()
}
}

View File

@ -25,7 +25,8 @@ public class FlutterWebViewFactory: NSObject, FlutterPlatformViewFactory {
let webviewController = FlutterWebViewController(registrar: registrar!,
withFrame: frame,
viewIdentifier: viewId,
arguments: arguments!)
params: arguments!)
webviewController.makeInitialLoad(params: arguments!)
return webviewController
}
}

View File

@ -53,6 +53,8 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
var callAsyncJavaScriptBelowIOS14Results: [String:((Any?) -> Void)] = [:]
var oldZoomScale = Float(1.0)
init(frame: CGRect, configuration: WKWebViewConfiguration, contextMenu: [String: Any]?, channel: FlutterMethodChannel?, userScripts: [UserScript] = []) {
super.init(frame: frame, configuration: configuration)
self.channel = channel
@ -270,6 +272,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
scrollView.addGestureRecognizer(self.longPressRecognizer)
scrollView.addGestureRecognizer(self.recognizerForDisablingContextMenuOnLinks)
scrollView.addObserver(self, forKeyPath: #keyPath(UIScrollView.contentOffset), options: [.new, .old], context: nil)
scrollView.addObserver(self, forKeyPath: #keyPath(UIScrollView.zoomScale), options: [.new, .old], context: nil)
addObserver(self,
forKeyPath: #keyPath(WKWebView.estimatedProgress),
@ -2056,6 +2059,14 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
}
}
public func scrollViewDidZoom(_ scrollView: UIScrollView) {
let newScale = Float(scrollView.zoomScale)
if newScale != oldZoomScale {
self.onZoomScaleChanged(newScale: newScale, oldScale: oldZoomScale)
oldZoomScale = newScale
}
}
public func webView(_ webView: WKWebView,
createWebViewWith configuration: WKWebViewConfiguration,
for navigationAction: WKNavigationAction,
@ -2306,6 +2317,11 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
channel?.invokeMethod("onScrollChanged", arguments: arguments)
}
public func onZoomScaleChanged(newScale: Float, oldScale: Float) {
let arguments: [String: Any] = ["newScale": newScale, "oldScale": oldScale]
channel?.invokeMethod("onZoomScaleChanged", arguments: arguments)
}
public func onOverScrolled(x: Int, y: Int, clampedX: Bool, clampedY: Bool) {
let arguments: [String: Any] = ["x": x, "y": y, "clampedX": clampedX, "clampedY": clampedY]
channel?.invokeMethod("onOverScrolled", arguments: arguments)
@ -2678,7 +2694,7 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
scrollView.setZoomScale(currentZoomScale * CGFloat(zoomFactor), animated: animated)
}
public func getScale() -> Float {
public func getZoomScale() -> Float {
return Float(scrollView.zoomScale)
}
@ -2869,6 +2885,7 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
imp_removeBlock(imp)
}
scrollView.removeObserver(self, forKeyPath: #keyPath(UIScrollView.contentOffset))
scrollView.removeObserver(self, forKeyPath: #keyPath(UIScrollView.zoomScale))
longPressRecognizer.removeTarget(self, action: #selector(longPressGestureDetected))
longPressRecognizer.delegate = nil
scrollView.removeGestureRecognizer(longPressRecognizer)

View File

@ -293,8 +293,8 @@ public class InAppWebViewMethodHandler: FlutterMethodCallDelegate {
webView?.reloadFromOrigin()
result(true)
break
case "getScale":
result(webView?.getScale())
case "getZoomScale":
result(webView?.getZoomScale())
break
case "hasOnlySecureContent":
result(webView?.hasOnlySecureContent ?? false)

View File

@ -0,0 +1,35 @@
//
// Size.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 26/03/21.
//
import Foundation
public class Size2D : NSObject {
var width: Double
var height: Double
public init(width: Double, height: Double) {
self.width = width
self.height = height
}
public static func fromMap(map: [String:Any?]?) -> Size2D? {
guard let map = map else {
return nil
}
return Size2D(
width: map["width"] as? Double ?? -1.0,
height: map["height"] as? Double ?? -1.0
)
}
public func toMap() -> [String:Any?] {
return [
"width": width,
"height": height
]
}
}

View File

@ -59,7 +59,7 @@ class InAppBrowser {
const MethodChannel('com.pichillilorenzo/flutter_inappbrowser');
/// WebView Controller that can be used to access the [InAppWebViewController] API.
late InAppWebViewController webViewController;
late final InAppWebViewController webViewController;
///The window id of a [CreateWindowAction.windowId].
final int? windowId;
@ -620,6 +620,19 @@ class InAppBrowser {
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#onOverScrolled(int,%20int,%20boolean,%20boolean)
void onOverScrolled(int x, int y, bool clampedX, bool clampedY) {}
///Event fired when the zoom scale of the WebView has changed.
///
///[oldScale] The old zoom scale factor.
///
///[newScale] The new zoom scale factor.
///
///**NOTE**: available only on Android.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onScaleChanged(android.webkit.WebView,%20float,%20float)
///
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiscrollviewdelegate/1619409-scrollviewdidzoom
void onZoomScaleChanged(double oldScale, double newScale) {}
///Event fired when the WebView notifies that a loading URL has been flagged by Safe Browsing.
///The default behavior is to show an interstitial to the user, with the reporting checkbox visible.
///
@ -738,15 +751,8 @@ class InAppBrowser {
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onFormResubmission(android.webkit.WebView,%20android.os.Message,%20android.os.Message)
Future<FormResubmissionAction?>? androidOnFormResubmission(Uri? url) {}
///Event fired when the scale applied to the WebView has changed.
///
///[oldScale] The old scale factor.
///
///[newScale] The new scale factor.
///
///**NOTE**: available only on Android.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onScaleChanged(android.webkit.WebView,%20float,%20float)
///Use [onZoomScaleChanged] instead.
@Deprecated('Use `onZoomScaleChanged` instead')
void androidOnScaleChanged(double oldScale, double newScale) {}
///Event fired when there is new favicon for the current page.

View File

@ -1,5 +1,6 @@
import 'dart:collection';
import 'dart:typed_data';
import 'dart:ui';
import 'package:flutter/services.dart';
import 'package:flutter_inappwebview/src/util.dart';
@ -11,6 +12,7 @@ import 'in_app_webview_controller.dart';
import 'in_app_webview_options.dart';
import '../pull_to_refresh/pull_to_refresh_controller.dart';
import '../pull_to_refresh/pull_to_refresh_options.dart';
import '../util.dart';
///Class that represents a WebView in headless mode.
///It can be used to run a WebView in background without attaching an `InAppWebView` to the widget tree.
@ -23,17 +25,33 @@ class HeadlessInAppWebView implements WebView {
bool _started = false;
bool _running = false;
static const MethodChannel _sharedChannel =
const MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview');
static const MethodChannel _sharedChannel = const MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview');
late MethodChannel _channel;
///WebView Controller that can be used to access the [InAppWebViewController] API.
late InAppWebViewController webViewController;
late final InAppWebViewController webViewController;
///The window id of a [CreateWindowAction.windowId].
final int? windowId;
///The WebView initial size in pixels.
///
///Set `-1` to match the corresponding width or height of the current device screen size.
///`Size(-1, -1)` will match both width and height of the current device screen size.
///
///**NOTE for Android**: `Size` width and height values will be converted to `int` values because they cannot have `double` values.
final Size initialSize;
HeadlessInAppWebView(
{this.windowId,
{this.initialSize = const Size(-1, -1),
this.windowId,
this.initialUrlRequest,
this.initialFile,
this.initialData,
this.initialOptions,
this.contextMenu,
this.initialUserScripts,
this.pullToRefreshController,
this.onWebViewCreated,
this.onLoadStart,
this.onLoadStop,
@ -78,6 +96,7 @@ class HeadlessInAppWebView implements WebView {
this.androidOnRenderProcessResponsive,
this.androidOnRenderProcessUnresponsive,
this.androidOnFormResubmission,
@Deprecated('Use `onZoomScaleChanged` instead')
this.androidOnScaleChanged,
this.androidOnReceivedIcon,
this.androidOnReceivedTouchIconUrl,
@ -86,29 +105,26 @@ class HeadlessInAppWebView implements WebView {
this.iosOnWebContentProcessDidTerminate,
this.iosOnDidReceiveServerRedirectForProvisionalNavigation,
this.iosOnNavigationResponse,
this.iosShouldAllowDeprecatedTLS,
this.initialUrlRequest,
this.initialFile,
this.initialData,
this.initialOptions,
this.contextMenu,
this.initialUserScripts,
this.pullToRefreshController}) {
this.iosShouldAllowDeprecatedTLS}) {
id = IdGenerator.generate();
webViewController = new InAppWebViewController(id, this);
this._channel = MethodChannel(
'com.pichillilorenzo/flutter_headless_inappwebview_$id');
this._channel.setMethodCallHandler(handleMethod);
}
Future<dynamic> handleMethod(MethodCall call) async {
switch (call.method) {
case "onHeadlessWebViewCreated":
case "onWebViewCreated":
pullToRefreshController?.initMethodChannel(id);
if (onWebViewCreated != null) {
onWebViewCreated!(webViewController);
}
break;
default:
return webViewController.handleMethod(call);
throw UnimplementedError("Unimplemented ${call.method} method");
}
return null;
}
///Runs the headless WebView.
@ -134,9 +150,10 @@ class HeadlessInAppWebView implements WebView {
this.initialUserScripts?.map((e) => e.toMap()).toList() ?? [],
'pullToRefreshOptions':
this.pullToRefreshController?.options.toMap() ??
PullToRefreshOptions(enabled: false).toMap()
PullToRefreshOptions(enabled: false).toMap(),
'initialSize': this.initialSize.toMap()
});
await _sharedChannel.invokeMethod('createHeadlessWebView', args);
await _sharedChannel.invokeMethod('run', args);
_running = true;
}
@ -146,8 +163,7 @@ class HeadlessInAppWebView implements WebView {
return;
}
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('id', () => id);
await _sharedChannel.invokeMethod('disposeHeadlessWebView', args);
await _channel.invokeMethod('dispose', args);
_started = false;
_running = false;
}
@ -157,26 +173,24 @@ class HeadlessInAppWebView implements WebView {
return _running;
}
@override
final void Function(InAppWebViewController controller)?
androidOnGeolocationPermissionsHidePrompt;
///Set the size of the WebView in pixels.
///
///Set `-1` to match the corresponding width or height of the current device screen size.
///`Size(-1, -1)` will match both width and height of the current device screen size.
///
///**NOTE for Android**: `Size` width and height values will be converted to `int` values because they cannot have `double` values.
Future<void> setSize(Size size) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('size', () => size.toMap());
await _channel.invokeMethod('setSize', args);
}
@override
final Future<GeolocationPermissionShowPromptResponse?> Function(
InAppWebViewController controller, String origin)?
androidOnGeolocationPermissionsShowPrompt;
@override
final Future<PermissionRequestResponse?> Function(
InAppWebViewController controller,
String origin,
List<String> resources)? androidOnPermissionRequest;
@override
final Future<SafeBrowsingResponse?> Function(
InAppWebViewController controller,
Uri url,
SafeBrowsingThreat? threatType)? androidOnSafeBrowsingHit;
///Gets the current size in pixels of the WebView.
Future<Size?> getSize() async {
Map<String, dynamic> args = <String, dynamic>{};
Map<String, dynamic> sizeMap = (await _channel.invokeMethod('getSize', args))?.cast<String, dynamic>();
return MapSize.fromMap(sizeMap);
}
@override
final InAppWebViewInitialData? initialData;
@ -200,211 +214,239 @@ class HeadlessInAppWebView implements WebView {
final PullToRefreshController? pullToRefreshController;
@override
final void Function(InAppWebViewController controller, Uri? url)?
void Function(InAppWebViewController controller)?
androidOnGeolocationPermissionsHidePrompt;
@override
Future<GeolocationPermissionShowPromptResponse?> Function(
InAppWebViewController controller, String origin)?
androidOnGeolocationPermissionsShowPrompt;
@override
Future<PermissionRequestResponse?> Function(
InAppWebViewController controller,
String origin,
List<String> resources)? androidOnPermissionRequest;
@override
Future<SafeBrowsingResponse?> Function(
InAppWebViewController controller,
Uri url,
SafeBrowsingThreat? threatType)? androidOnSafeBrowsingHit;
@override
void Function(InAppWebViewController controller, Uri? url)?
onPageCommitVisible;
@override
final void Function(InAppWebViewController controller, String? title)?
void Function(InAppWebViewController controller, String? title)?
onTitleChanged;
@override
final void Function(InAppWebViewController controller)?
void Function(InAppWebViewController controller)?
iosOnDidReceiveServerRedirectForProvisionalNavigation;
@override
final void Function(InAppWebViewController controller)?
void Function(InAppWebViewController controller)?
iosOnWebContentProcessDidTerminate;
@override
final Future<IOSNavigationResponseAction?> Function(
Future<IOSNavigationResponseAction?> Function(
InAppWebViewController controller,
IOSWKNavigationResponse navigationResponse)? iosOnNavigationResponse;
@override
final Future<IOSShouldAllowDeprecatedTLSAction?> Function(
Future<IOSShouldAllowDeprecatedTLSAction?> Function(
InAppWebViewController controller,
URLAuthenticationChallenge challenge)? iosShouldAllowDeprecatedTLS;
@override
final Future<AjaxRequestAction> Function(
Future<AjaxRequestAction> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest)?
onAjaxProgress;
@override
final Future<AjaxRequestAction?> Function(
Future<AjaxRequestAction?> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest)?
onAjaxReadyStateChange;
@override
final void Function(
void Function(
InAppWebViewController controller, ConsoleMessage consoleMessage)?
onConsoleMessage;
@override
final Future<bool?> Function(InAppWebViewController controller,
Future<bool?> Function(InAppWebViewController controller,
CreateWindowAction createWindowAction)? onCreateWindow;
@override
final void Function(InAppWebViewController controller)? onCloseWindow;
void Function(InAppWebViewController controller)? onCloseWindow;
@override
final void Function(InAppWebViewController controller)? onWindowFocus;
void Function(InAppWebViewController controller)? onWindowFocus;
@override
final void Function(InAppWebViewController controller)? onWindowBlur;
void Function(InAppWebViewController controller)? onWindowBlur;
@override
final void Function(InAppWebViewController controller, Uri url)?
void Function(InAppWebViewController controller, Uri url)?
onDownloadStart;
@override
final void Function(InAppWebViewController controller, int activeMatchOrdinal,
void Function(InAppWebViewController controller, int activeMatchOrdinal,
int numberOfMatches, bool isDoneCounting)? onFindResultReceived;
@override
final Future<JsAlertResponse?> Function(
Future<JsAlertResponse?> Function(
InAppWebViewController controller, JsAlertRequest jsAlertRequest)?
onJsAlert;
@override
final Future<JsConfirmResponse?> Function(
Future<JsConfirmResponse?> Function(
InAppWebViewController controller, JsConfirmRequest jsConfirmRequest)?
onJsConfirm;
@override
final Future<JsPromptResponse?> Function(
Future<JsPromptResponse?> Function(
InAppWebViewController controller, JsPromptRequest jsPromptRequest)?
onJsPrompt;
@override
final void Function(InAppWebViewController controller, Uri? url, int code,
void Function(InAppWebViewController controller, Uri? url, int code,
String message)? onLoadError;
@override
final void Function(InAppWebViewController controller, Uri? url,
void Function(InAppWebViewController controller, Uri? url,
int statusCode, String description)? onLoadHttpError;
@override
final void Function(
void Function(
InAppWebViewController controller, LoadedResource resource)?
onLoadResource;
@override
final Future<CustomSchemeResponse?> Function(
Future<CustomSchemeResponse?> Function(
InAppWebViewController controller, Uri url)? onLoadResourceCustomScheme;
@override
final void Function(InAppWebViewController controller, Uri? url)? onLoadStart;
void Function(InAppWebViewController controller, Uri? url)? onLoadStart;
@override
final void Function(InAppWebViewController controller, Uri? url)? onLoadStop;
void Function(InAppWebViewController controller, Uri? url)? onLoadStop;
@override
final void Function(InAppWebViewController controller,
void Function(InAppWebViewController controller,
InAppWebViewHitTestResult hitTestResult)? onLongPressHitTestResult;
@override
final void Function(InAppWebViewController controller, Uri? url)? onPrint;
void Function(InAppWebViewController controller, Uri? url)? onPrint;
@override
final void Function(InAppWebViewController controller, int progress)?
void Function(InAppWebViewController controller, int progress)?
onProgressChanged;
@override
final Future<ClientCertResponse?> Function(InAppWebViewController controller,
Future<ClientCertResponse?> Function(InAppWebViewController controller,
URLAuthenticationChallenge challenge)? onReceivedClientCertRequest;
@override
final Future<HttpAuthResponse?> Function(InAppWebViewController controller,
Future<HttpAuthResponse?> Function(InAppWebViewController controller,
URLAuthenticationChallenge challenge)? onReceivedHttpAuthRequest;
@override
final Future<ServerTrustAuthResponse?> Function(
Future<ServerTrustAuthResponse?> Function(
InAppWebViewController controller,
URLAuthenticationChallenge challenge)? onReceivedServerTrustAuthRequest;
@override
final void Function(InAppWebViewController controller, int x, int y)?
void Function(InAppWebViewController controller, int x, int y)?
onScrollChanged;
@override
final void Function(
void Function(
InAppWebViewController controller, Uri? url, bool? androidIsReload)?
onUpdateVisitedHistory;
@override
final void Function(InAppWebViewController controller)? onWebViewCreated;
void Function(InAppWebViewController controller)? onWebViewCreated;
@override
final Future<AjaxRequest?> Function(
Future<AjaxRequest?> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest)?
shouldInterceptAjaxRequest;
@override
final Future<FetchRequest?> Function(
Future<FetchRequest?> Function(
InAppWebViewController controller, FetchRequest fetchRequest)?
shouldInterceptFetchRequest;
@override
final Future<NavigationActionPolicy?> Function(
Future<NavigationActionPolicy?> Function(
InAppWebViewController controller, NavigationAction navigationAction)?
shouldOverrideUrlLoading;
@override
final void Function(InAppWebViewController controller)? onEnterFullscreen;
void Function(InAppWebViewController controller)? onEnterFullscreen;
@override
final void Function(InAppWebViewController controller)? onExitFullscreen;
void Function(InAppWebViewController controller)? onExitFullscreen;
@override
final void Function(InAppWebViewController controller, int x, int y,
void Function(InAppWebViewController controller, int x, int y,
bool clampedX, bool clampedY)? onOverScrolled;
@override
final Future<WebResourceResponse?> Function(
void Function(
InAppWebViewController controller, double oldScale, double newScale)?
onZoomScaleChanged;
@override
Future<WebResourceResponse?> Function(
InAppWebViewController controller, WebResourceRequest request)?
androidShouldInterceptRequest;
@override
final Future<WebViewRenderProcessAction?> Function(
Future<WebViewRenderProcessAction?> Function(
InAppWebViewController controller, Uri? url)?
androidOnRenderProcessUnresponsive;
@override
final Future<WebViewRenderProcessAction?> Function(
Future<WebViewRenderProcessAction?> Function(
InAppWebViewController controller, Uri? url)?
androidOnRenderProcessResponsive;
@override
final void Function(
void Function(
InAppWebViewController controller, RenderProcessGoneDetail detail)?
androidOnRenderProcessGone;
@override
final Future<FormResubmissionAction?> Function(
Future<FormResubmissionAction?> Function(
InAppWebViewController controller, Uri? url)? androidOnFormResubmission;
///Use [onZoomScaleChanged] instead.
@Deprecated('Use `onZoomScaleChanged` instead')
@override
final void Function(
void Function(
InAppWebViewController controller, double oldScale, double newScale)?
androidOnScaleChanged;
@override
final void Function(InAppWebViewController controller, Uint8List icon)?
void Function(InAppWebViewController controller, Uint8List icon)?
androidOnReceivedIcon;
@override
final void Function(
void Function(
InAppWebViewController controller, Uri url, bool precomposed)?
androidOnReceivedTouchIconUrl;
@override
final Future<JsBeforeUnloadResponse?> Function(
Future<JsBeforeUnloadResponse?> Function(
InAppWebViewController controller,
JsBeforeUnloadRequest jsBeforeUnloadRequest)? androidOnJsBeforeUnload;
@override
final void Function(
void Function(
InAppWebViewController controller, LoginRequest loginRequest)?
androidOnReceivedLoginRequest;
}

View File

@ -77,6 +77,7 @@ class InAppWebView extends StatefulWidget implements WebView {
this.onWindowFocus,
this.onWindowBlur,
this.onOverScrolled,
this.onZoomScaleChanged,
this.androidOnSafeBrowsingHit,
this.androidOnPermissionRequest,
this.androidOnGeolocationPermissionsShowPrompt,
@ -86,6 +87,7 @@ class InAppWebView extends StatefulWidget implements WebView {
this.androidOnRenderProcessResponsive,
this.androidOnRenderProcessUnresponsive,
this.androidOnFormResubmission,
@Deprecated('Use `onZoomScaleChanged` instead')
this.androidOnScaleChanged,
this.androidOnReceivedIcon,
this.androidOnReceivedTouchIconUrl,
@ -313,6 +315,11 @@ class InAppWebView extends StatefulWidget implements WebView {
final void Function(InAppWebViewController controller, int x, int y,
bool clampedX, bool clampedY)? onOverScrolled;
@override
final void Function(
InAppWebViewController controller, double oldScale, double newScale)?
onZoomScaleChanged;
@override
final Future<WebResourceResponse?> Function(
InAppWebViewController controller, WebResourceRequest request)?
@ -337,6 +344,8 @@ class InAppWebView extends StatefulWidget implements WebView {
final Future<FormResubmissionAction?> Function(
InAppWebViewController controller, Uri? url)? androidOnFormResubmission;
///Use [onZoomScaleChanged] instead.
@Deprecated('Use `onZoomScaleChanged` instead')
@override
final void Function(
InAppWebViewController controller, double oldScale, double newScale)?

View File

@ -101,12 +101,6 @@ class InAppWebViewController {
Future<dynamic> handleMethod(MethodCall call) async {
switch (call.method) {
case "onHeadlessWebViewCreated":
if (_webview != null &&
_webview is HeadlessInAppWebView &&
_webview!.onWebViewCreated != null)
_webview!.onWebViewCreated!(this);
break;
case "onLoadStart":
if ((_webview != null && _webview!.onLoadStart != null) ||
_inAppBrowser != null) {
@ -361,13 +355,17 @@ class InAppWebViewController {
?.toMap();
}
break;
case "onScaleChanged":
if ((_webview != null && _webview!.androidOnScaleChanged != null) ||
case "onZoomScaleChanged":
if ((_webview != null && (_webview!.androidOnScaleChanged != null || _webview!.onZoomScaleChanged != null)) ||
_inAppBrowser != null) {
double oldScale = call.arguments["oldScale"];
double newScale = call.arguments["newScale"];
if (_webview != null && _webview!.androidOnScaleChanged != null)
_webview!.androidOnScaleChanged!(this, oldScale, newScale);
if (_webview != null) {
if (_webview!.onZoomScaleChanged != null)
_webview!.onZoomScaleChanged!(this, oldScale, newScale);
else
_webview!.androidOnScaleChanged!(this, oldScale, newScale);
}
else
_inAppBrowser!.androidOnScaleChanged(oldScale, newScale);
}
@ -1703,16 +1701,18 @@ class InAppWebViewController {
return await _channel.invokeMethod('zoomBy', args);
}
///Gets the current scale of this WebView.
///
///**Official Android API**:
///- https://developer.android.com/reference/android/util/DisplayMetrics#density
///- https://developer.android.com/reference/android/webkit/WebViewClient#onScaleChanged(android.webkit.WebView,%20float,%20float)
///Gets the current zoom scale of the WebView.
///
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiscrollview/1619419-zoomscale
Future<double?> getScale() async {
Future<double?> getZoomScale() async {
Map<String, dynamic> args = <String, dynamic>{};
return await _channel.invokeMethod('getScale', args);
return await _channel.invokeMethod('getZoomScale', args);
}
///Use [getZoomScale] instead.
@Deprecated('Use `getZoomScale` instead')
Future<double?> getScale() async {
return await getZoomScale();
}
///Gets the selected text.

View File

@ -401,6 +401,21 @@ abstract class WebView {
final void Function(InAppWebViewController controller, int x, int y,
bool clampedX, bool clampedY)? onOverScrolled;
///Event fired when the zoom scale of the WebView has changed.
///
///[oldScale] The old zoom scale factor.
///
///[newScale] The new zoom scale factor.
///
///**NOTE**: available only on Android.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onScaleChanged(android.webkit.WebView,%20float,%20float)
///
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiscrollviewdelegate/1619409-scrollviewdidzoom
final void Function(
InAppWebViewController controller, double oldScale, double newScale)?
onZoomScaleChanged;
///Event fired when the webview notifies that a loading URL has been flagged by Safe Browsing.
///The default behavior is to show an interstitial to the user, with the reporting checkbox visible.
///
@ -531,15 +546,8 @@ abstract class WebView {
final Future<FormResubmissionAction?> Function(
InAppWebViewController controller, Uri? url)? androidOnFormResubmission;
///Event fired when the scale applied to the WebView has changed.
///
///[oldScale] The old scale factor.
///
///[newScale] The new scale factor.
///
///**NOTE**: available only on Android.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onScaleChanged(android.webkit.WebView,%20float,%20float)
///Use [onZoomScaleChanged] instead.
@Deprecated('Use `onZoomScaleChanged` instead')
final void Function(
InAppWebViewController controller, double oldScale, double newScale)?
androidOnScaleChanged;
@ -702,6 +710,7 @@ abstract class WebView {
this.onWindowFocus,
this.onWindowBlur,
this.onOverScrolled,
this.onZoomScaleChanged,
this.androidOnSafeBrowsingHit,
this.androidOnPermissionRequest,
this.androidOnGeolocationPermissionsShowPrompt,
@ -711,6 +720,7 @@ abstract class WebView {
this.androidOnRenderProcessResponsive,
this.androidOnRenderProcessUnresponsive,
this.androidOnFormResubmission,
@Deprecated('Use `onZoomScaleChanged` instead')
this.androidOnScaleChanged,
this.androidOnReceivedIcon,
this.androidOnReceivedTouchIconUrl,

View File

@ -485,3 +485,23 @@ extension HexColor on Color {
'${green.toRadixString(16).padLeft(2, '0')}'
'${blue.toRadixString(16).padLeft(2, '0')}';
}
extension MapSize on Size {
static Size? fromMap(Map<String, dynamic>? map) {
if (map == null) {
return null;
}
return Size(map["width"] ?? -1.0, map["height"] ?? -1.0);
}
Map<String, double> toJson() {
return toMap();
}
Map<String, double> toMap() {
return {
'width': width,
'height': height
};
}
}

View File

@ -1,6 +1,6 @@
name: flutter_inappwebview
description: A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window.
version: 5.2.1+1
version: 5.3.0
homepage: https://github.com/pichillilorenzo/flutter_inappwebview
environment: