diff --git a/.idea/libraries/Dart_Packages.xml b/.idea/libraries/Dart_Packages.xml
deleted file mode 100644
index 3b0b4626..00000000
--- a/.idea/libraries/Dart_Packages.xml
+++ /dev/null
@@ -1,420 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/libraries/Flutter_Plugins.xml b/.idea/libraries/Flutter_Plugins.xml
index 31799730..65bb3679 100755
--- a/.idea/libraries/Flutter_Plugins.xml
+++ b/.idea/libraries/Flutter_Plugins.xml
@@ -1,6 +1,8 @@
-
+
+
+
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b76c4e32..0d925a7a 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebViewFlutterPlugin.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebViewFlutterPlugin.java
index cf44bf6e..574b803c 100755
--- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebViewFlutterPlugin.java
+++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebViewFlutterPlugin.java
@@ -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;
diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebViewMethodHandler.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebViewMethodHandler.java
index 5c4ef366..3f4737e9 100644
--- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebViewMethodHandler.java
+++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebViewMethodHandler.java
@@ -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) {
diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/Util.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/Util.java
index d88d3d6b..bccb1b6d 100755
--- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/Util.java
+++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/Util.java
@@ -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;
+ }
}
diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/headless_in_app_webview/HeadlessInAppWebView.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/headless_in_app_webview/HeadlessInAppWebView.java
new file mode 100644
index 00000000..e2c3d431
--- /dev/null
+++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/headless_in_app_webview/HeadlessInAppWebView.java
@@ -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 sizeMap = (Map) 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 obj = new HashMap<>();
+ channel.invokeMethod("onWebViewCreated", obj);
+ }
+
+ public void prepare(Map 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 initialSize = (Map) 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;
+ }
+}
diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/HeadlessInAppWebViewManager.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/headless_in_app_webview/HeadlessInAppWebViewManager.java
similarity index 71%
rename from android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/HeadlessInAppWebViewManager.java
rename to android/src/main/java/com/pichillilorenzo/flutter_inappwebview/headless_in_app_webview/HeadlessInAppWebViewManager.java
index 78514268..241142b8 100755
--- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/HeadlessInAppWebViewManager.java
+++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/headless_in_app_webview/HeadlessInAppWebViewManager.java
@@ -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 flutterWebViews = new HashMap<>();
+ public MethodChannel channel;
+ public static final Map 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 params = (HashMap) 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 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 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);
}
}
diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/FlutterWebView.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/FlutterWebView.java
index 7bfa2e71..c5915973 100755
--- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/FlutterWebView.java
+++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/FlutterWebView.java
@@ -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 params, View containerView) {
+ public FlutterWebView(BinaryMessenger messenger, final Context context, Object id,
+ HashMap 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 initialUrlRequest = (Map) params.get("initialUrlRequest");
- final String initialFile = (String) params.get("initialFile");
- final Map initialData = (Map) params.get("initialData");
+
Map initialOptions = (Map) params.get("initialOptions");
Map contextMenu = (Map) 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 params) {
+ Integer windowId = (Integer) params.get("windowId");
+ Map initialUrlRequest = (Map) params.get("initialUrlRequest");
+ final String initialFile = (String) params.get("initialFile");
+ final Map initialData = (Map) 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 obj = new HashMap<>();
- channel.invokeMethod("onHeadlessWebViewCreated", obj);
- }
- }
-
- @Override
- public View getView() {
- return pullToRefreshLayout != null ? pullToRefreshLayout : webView;
}
@Override
diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/FlutterWebViewFactory.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/FlutterWebViewFactory.java
index 4d85ef86..f515d067 100755
--- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/FlutterWebViewFactory.java
+++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/FlutterWebViewFactory.java
@@ -23,7 +23,9 @@ public class FlutterWebViewFactory extends PlatformViewFactory {
@Override
public PlatformView create(Context context, int id, Object args) {
HashMap params = (HashMap) args;
- return new FlutterWebView(messenger, context, id, params, containerView);
+ FlutterWebView flutterWebView = new FlutterWebView(messenger, context, id, params, containerView);
+ flutterWebView.makeInitialLoad(params);
+ return flutterWebView;
}
}
diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebView.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebView.java
index c9322498..6ce140a5 100755
--- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebView.java
+++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebView.java
@@ -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 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 rect = (Map) 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 {
diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebViewClient.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebViewClient.java
index 7616bc5b..0b71c3b1 100755
--- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebViewClient.java
+++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebViewClient.java
@@ -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 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)
diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/Size2D.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/Size2D.java
new file mode 100644
index 00000000..2956a91c
--- /dev/null
+++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/Size2D.java
@@ -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 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 toMap() {
+ Map 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 +
+ '}';
+ }
+}
diff --git a/example/integration_test/webview_flutter_test.dart b/example/integration_test/webview_flutter_test.dart
index 3c17b147..e944839c 100644
--- a/example/integration_test/webview_flutter_test.dart
+++ b/example/integration_test/webview_flutter_test.dart
@@ -3247,6 +3247,45 @@ setTimeout(function() {
await expectLater(onTitleChangedCompleter.future, completes);
});
+ testWidgets('onZoomScaleChanged', (WidgetTester tester) async {
+ final Completer controllerCompleter = Completer();
+ final Completer pageLoaded = Completer();
+ final Completer onZoomScaleChangedCompleter = Completer();
+
+ 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();
final Completer pageLoaded = Completer();
@@ -3341,50 +3380,6 @@ setTimeout(function() {
expect(resourceLoaded, containsAll(resourceList));
}, skip: !Platform.isAndroid);
- testWidgets('androidOnScaleChanged', (WidgetTester tester) async {
- final Completer controllerCompleter = Completer();
- final Completer pageLoaded = Completer();
- final Completer onScaleChangedCompleter = Completer();
-
- 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();
final Completer pageLoaded = Completer();
@@ -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();
final Completer pageLoaded = Completer();
@@ -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);
});
diff --git a/flutter_inappwebview.iml b/flutter_inappwebview.iml
index 4cb39159..0adae5aa 100755
--- a/flutter_inappwebview.iml
+++ b/flutter_inappwebview.iml
@@ -80,6 +80,5 @@
-
\ No newline at end of file
diff --git a/ios/Classes/HeadlessInAppWebView/HeadlessInAppWebView.swift b/ios/Classes/HeadlessInAppWebView/HeadlessInAppWebView.swift
new file mode 100644
index 00000000..6fb9a1be
--- /dev/null
+++ b/ios/Classes/HeadlessInAppWebView/HeadlessInAppWebView.swift
@@ -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
+ }
+}
diff --git a/ios/Classes/InAppWebView/HeadlessInAppWebViewManager.swift b/ios/Classes/HeadlessInAppWebView/HeadlessInAppWebViewManager.swift
similarity index 65%
rename from ios/Classes/InAppWebView/HeadlessInAppWebViewManager.swift
rename to ios/Classes/HeadlessInAppWebView/HeadlessInAppWebViewManager.swift
index 9bb1aa17..0bcc777c 100644
--- a/ios/Classes/InAppWebView/HeadlessInAppWebViewManager.swift
+++ b/ios/Classes/HeadlessInAppWebView/HeadlessInAppWebViewManager.swift
@@ -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)
}
}
diff --git a/ios/Classes/InAppWebView/FlutterWebViewController.swift b/ios/Classes/InAppWebView/FlutterWebViewController.swift
index 0ebe03e9..c2a3dd0b 100755
--- a/ios/Classes/InAppWebView/FlutterWebViewController.swift
+++ b/ios/Classes/InAppWebView/FlutterWebViewController.swift
@@ -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()
+ }
}
diff --git a/ios/Classes/InAppWebView/FlutterWebViewFactory.swift b/ios/Classes/InAppWebView/FlutterWebViewFactory.swift
index 181441f0..30fcce52 100755
--- a/ios/Classes/InAppWebView/FlutterWebViewFactory.swift
+++ b/ios/Classes/InAppWebView/FlutterWebViewFactory.swift
@@ -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
}
}
diff --git a/ios/Classes/InAppWebView/InAppWebView.swift b/ios/Classes/InAppWebView/InAppWebView.swift
index bd752306..d28a8904 100755
--- a/ios/Classes/InAppWebView/InAppWebView.swift
+++ b/ios/Classes/InAppWebView/InAppWebView.swift
@@ -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)
diff --git a/ios/Classes/InAppWebViewMethodHandler.swift b/ios/Classes/InAppWebViewMethodHandler.swift
index d65d58aa..ae3d0b60 100644
--- a/ios/Classes/InAppWebViewMethodHandler.swift
+++ b/ios/Classes/InAppWebViewMethodHandler.swift
@@ -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)
diff --git a/ios/Classes/Types/Size2D.swift b/ios/Classes/Types/Size2D.swift
new file mode 100644
index 00000000..7c2b9c6c
--- /dev/null
+++ b/ios/Classes/Types/Size2D.swift
@@ -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
+ ]
+ }
+}
diff --git a/lib/src/in_app_browser/in_app_browser.dart b/lib/src/in_app_browser/in_app_browser.dart
index f60753da..ff4e2877 100755
--- a/lib/src/in_app_browser/in_app_browser.dart
+++ b/lib/src/in_app_browser/in_app_browser.dart
@@ -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? 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.
diff --git a/lib/src/in_app_webview/headless_in_app_webview.dart b/lib/src/in_app_webview/headless_in_app_webview.dart
index f544527e..33f8d208 100644
--- a/lib/src/in_app_webview/headless_in_app_webview.dart
+++ b/lib/src/in_app_webview/headless_in_app_webview.dart
@@ -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 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 args = {};
- 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 setSize(Size size) async {
+ Map args = {};
+ args.putIfAbsent('size', () => size.toMap());
+ await _channel.invokeMethod('setSize', args);
+ }
- @override
- final Future Function(
- InAppWebViewController controller, String origin)?
- androidOnGeolocationPermissionsShowPrompt;
-
- @override
- final Future Function(
- InAppWebViewController controller,
- String origin,
- List resources)? androidOnPermissionRequest;
-
- @override
- final Future Function(
- InAppWebViewController controller,
- Uri url,
- SafeBrowsingThreat? threatType)? androidOnSafeBrowsingHit;
+ ///Gets the current size in pixels of the WebView.
+ Future getSize() async {
+ Map args = {};
+ Map sizeMap = (await _channel.invokeMethod('getSize', args))?.cast();
+ 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 Function(
+ InAppWebViewController controller, String origin)?
+ androidOnGeolocationPermissionsShowPrompt;
+
+ @override
+ Future Function(
+ InAppWebViewController controller,
+ String origin,
+ List resources)? androidOnPermissionRequest;
+
+ @override
+ Future 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 Function(
+ Future Function(
InAppWebViewController controller,
IOSWKNavigationResponse navigationResponse)? iosOnNavigationResponse;
@override
- final Future Function(
+ Future Function(
InAppWebViewController controller,
URLAuthenticationChallenge challenge)? iosShouldAllowDeprecatedTLS;
@override
- final Future Function(
+ Future Function(
InAppWebViewController controller, AjaxRequest ajaxRequest)?
onAjaxProgress;
@override
- final Future Function(
+ Future Function(
InAppWebViewController controller, AjaxRequest ajaxRequest)?
onAjaxReadyStateChange;
@override
- final void Function(
+ void Function(
InAppWebViewController controller, ConsoleMessage consoleMessage)?
onConsoleMessage;
@override
- final Future Function(InAppWebViewController controller,
+ Future 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 Function(
+ Future Function(
InAppWebViewController controller, JsAlertRequest jsAlertRequest)?
onJsAlert;
@override
- final Future Function(
+ Future Function(
InAppWebViewController controller, JsConfirmRequest jsConfirmRequest)?
onJsConfirm;
@override
- final Future Function(
+ Future 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 Function(
+ Future 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 Function(InAppWebViewController controller,
+ Future Function(InAppWebViewController controller,
URLAuthenticationChallenge challenge)? onReceivedClientCertRequest;
@override
- final Future Function(InAppWebViewController controller,
+ Future Function(InAppWebViewController controller,
URLAuthenticationChallenge challenge)? onReceivedHttpAuthRequest;
@override
- final Future Function(
+ Future 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 Function(
+ Future Function(
InAppWebViewController controller, AjaxRequest ajaxRequest)?
shouldInterceptAjaxRequest;
@override
- final Future Function(
+ Future Function(
InAppWebViewController controller, FetchRequest fetchRequest)?
shouldInterceptFetchRequest;
@override
- final Future Function(
+ Future 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 Function(
+ void Function(
+ InAppWebViewController controller, double oldScale, double newScale)?
+ onZoomScaleChanged;
+
+ @override
+ Future Function(
InAppWebViewController controller, WebResourceRequest request)?
androidShouldInterceptRequest;
@override
- final Future Function(
+ Future Function(
InAppWebViewController controller, Uri? url)?
androidOnRenderProcessUnresponsive;
@override
- final Future Function(
+ Future Function(
InAppWebViewController controller, Uri? url)?
androidOnRenderProcessResponsive;
@override
- final void Function(
+ void Function(
InAppWebViewController controller, RenderProcessGoneDetail detail)?
androidOnRenderProcessGone;
@override
- final Future Function(
+ Future 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 Function(
+ Future Function(
InAppWebViewController controller,
JsBeforeUnloadRequest jsBeforeUnloadRequest)? androidOnJsBeforeUnload;
@override
- final void Function(
+ void Function(
InAppWebViewController controller, LoginRequest loginRequest)?
androidOnReceivedLoginRequest;
}
diff --git a/lib/src/in_app_webview/in_app_webview.dart b/lib/src/in_app_webview/in_app_webview.dart
index 7c960b34..733cc083 100755
--- a/lib/src/in_app_webview/in_app_webview.dart
+++ b/lib/src/in_app_webview/in_app_webview.dart
@@ -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 Function(
InAppWebViewController controller, WebResourceRequest request)?
@@ -337,6 +344,8 @@ class InAppWebView extends StatefulWidget implements WebView {
final Future Function(
InAppWebViewController controller, Uri? url)? androidOnFormResubmission;
+ ///Use [onZoomScaleChanged] instead.
+ @Deprecated('Use `onZoomScaleChanged` instead')
@override
final void Function(
InAppWebViewController controller, double oldScale, double newScale)?
diff --git a/lib/src/in_app_webview/in_app_webview_controller.dart b/lib/src/in_app_webview/in_app_webview_controller.dart
index 386ecc0a..91beb5ec 100644
--- a/lib/src/in_app_webview/in_app_webview_controller.dart
+++ b/lib/src/in_app_webview/in_app_webview_controller.dart
@@ -101,12 +101,6 @@ class InAppWebViewController {
Future 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 getScale() async {
+ Future getZoomScale() async {
Map args = {};
- return await _channel.invokeMethod('getScale', args);
+ return await _channel.invokeMethod('getZoomScale', args);
+ }
+
+ ///Use [getZoomScale] instead.
+ @Deprecated('Use `getZoomScale` instead')
+ Future getScale() async {
+ return await getZoomScale();
}
///Gets the selected text.
diff --git a/lib/src/in_app_webview/webview.dart b/lib/src/in_app_webview/webview.dart
index d63bab68..3bc77ecf 100644
--- a/lib/src/in_app_webview/webview.dart
+++ b/lib/src/in_app_webview/webview.dart
@@ -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 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,
diff --git a/lib/src/util.dart b/lib/src/util.dart
index 9cf8b9dd..fb3192d5 100644
--- a/lib/src/util.dart
+++ b/lib/src/util.dart
@@ -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? map) {
+ if (map == null) {
+ return null;
+ }
+ return Size(map["width"] ?? -1.0, map["height"] ?? -1.0);
+ }
+
+ Map toJson() {
+ return toMap();
+ }
+
+ Map toMap() {
+ return {
+ 'width': width,
+ 'height': height
+ };
+ }
+}
\ No newline at end of file
diff --git a/pubspec.yaml b/pubspec.yaml
index a9bdf246..1c1731c5 100755
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -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: