added getScrollX and getScrollY webview methods, added HttpOnly and SameSite set cookie options, added animated option to scrollTo and scrollBy webview methods, Added error and message to the ServerTrustChallenge class for iOS, added contentInsetAdjustmentBehavior webview iOS-specific option, added getCertificate android-specific webview method, added copy and copyWithValue methods for webview class options

This commit is contained in:
Lorenzo Pichilli 2020-06-13 03:50:19 +02:00
parent 1b2de86375
commit b9d1482887
30 changed files with 1269 additions and 146 deletions

View File

@ -1,8 +1,15 @@
## 3.4.0
- Added `requestFocusNodeHref`, `requestImageRef`, `getMetaTags`, `getMetaThemeColor` webview methods
- Added `requestFocusNodeHref`, `requestImageRef`, `getMetaTags`, `getMetaThemeColor`, `getScrollX`, `getScrollY` webview methods
- Added `WebStorage`, `LocalStorage` and `SessionStorage` class to manage `window.localStorage` and `window.sessionStorage` JavaScript [Web Storage API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API)
- Added `supportZoom` webview option also on iOS
- Added `HttpOnly`, `SameSite` set cookie options
- Updated `Cookie` class
- Added `animated` option to `scrollTo` and `scrollBy` webview methods
- Added error and message to the `ServerTrustChallenge` class for iOS (class used by the `onReceivedServerTrustAuthRequest` event)
- Added `contentInsetAdjustmentBehavior` webview iOS-specific option
- Added `getCertificate` android-specific webview method
- Added `copy` and `copyWithValue` methods for webview class options
- Fixed `zoomBy`, `setOptions` webview methods on Android
- Fixed `databaseEnabled` android webview option default value to `true`
@ -12,6 +19,8 @@
- `getHtml` webview method now could return `null` if it was unable to get it.
- Moved `supportZoom` webview option to cross-platform
- `builtInZoomControls` android webview options changed default value to `true`
- Updated `ServerTrustChallenge` class used by the `onReceivedServerTrustAuthRequest` event
- The method `getOptions`could return null now
## 3.3.0+3

View File

@ -59,6 +59,10 @@ Because of [Flutter AndroidX compatibility](https://flutter.dev/docs/development
Also, note that to use the `InAppWebView` widget on Android, it requires **Android API 20+** (see [AndroidView](https://api.flutter.dev/flutter/widgets/AndroidView-class.html)).
**Support HTTP request**: Starting with Android 9 (API level 28), cleartext support is disabled by default:
- Check the official [Network security configuration - "Opt out of cleartext traffic"](https://developer.android.com/training/articles/security-config#CleartextTrafficPermitted) section.
- Also, check this StackOverflow issue answer: [Cleartext HTTP traffic not permitted](https://stackoverflow.com/a/50834600/4637638).
### IMPORTANT Note for iOS
If you are starting a new fresh app, you need to create the Flutter App with `flutter create --androidx -i swift`
@ -392,8 +396,8 @@ Screenshots:
* `clearMatches`: Clears the highlighting surrounding text matches created by `findAllAsync()`.
* `getTRexRunnerHtml`: Gets the html (with javascript) of the Chromium's t-rex runner game. Used in combination with `getTRexRunnerCss()`.
* `getTRexRunnerCss`: Gets the css of the Chromium's t-rex runner game. Used in combination with `getTRexRunnerHtml()`.
* `scrollTo({@required int x, @required int y})`: Scrolls the WebView to the position.
* `scrollBy({@required int x, @required int y})`: Moves the scrolled position of the WebView.
* `scrollTo({@required int x, @required int y, bool animated = false})`: Scrolls the WebView to the position.
* `scrollBy({@required int x, @required int y, bool animated = false})`: Moves the scrolled position of the WebView.
* `pauseTimers`: On Android, it pauses all layout, parsing, and JavaScript timers for all WebViews. This is a global requests, not restricted to just this WebView. This can be useful if the application has been paused. On iOS, it is restricted to just this WebView.
* `resumeTimers`: On Android, it resumes all layout, parsing, and JavaScript timers for all WebViews. This will resume dispatching all timers. On iOS, it resumes all layout, parsing, and JavaScript timers to just this WebView.
* `printCurrentPage`: Prints the current page.
@ -406,6 +410,8 @@ Screenshots:
* `requestImageRef`: Requests the URL of the image last touched by the user.
* `getMetaTags`: Returns the list of `<meta>` tags of the current WebView.
* `getMetaThemeColor`: Returns an instance of `Color` representing the `content` value of the `<meta name="theme-color" content="">` tag of the current WebView, if available, otherwise `null`.
* `getScrollX`: Returns the scrolled left position of the current WebView.
* `getScrollY`: Returns the scrolled top position of the current WebView.
* `static getDefaultUserAgent`: Gets the default user agent.
##### `InAppWebViewController` Android-specific methods
@ -587,6 +593,7 @@ Instead, on the `onLoadStop` WebView event, you can use `callHandler` directly:
* `isPagingEnabled`: A Boolean value that determines whether paging is enabled for the scroll view. The default value is `false`.
* `maximumZoomScale`: A floating-point value that specifies the maximum scale factor that can be applied to the scroll view's content. The default value is `1.0`.
* `minimumZoomScale`: A floating-point value that specifies the minimum scale factor that can be applied to the scroll view's content. The default value is `1.0`.
* `contentInsetAdjustmentBehavior`: Configures how safe area insets are added to the adjusted content inset. The default value is `IOSUIScrollViewContentInsetAdjustmentBehavior.NEVER`.
#### `InAppWebView` Events

View File

@ -280,7 +280,8 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
{
Integer x = (Integer) call.argument("x");
Integer y = (Integer) call.argument("y");
scrollTo(x, y);
Boolean animated = (Boolean) call.argument("animated");
scrollTo(x, y, animated);
}
result.success(true);
break;
@ -288,7 +289,8 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
{
Integer x = (Integer) call.argument("x");
Integer y = (Integer) call.argument("y");
scrollBy(x, y);
Boolean animated = (Boolean) call.argument("animated");
scrollBy(x, y, animated);
}
result.success(true);
break;
@ -376,6 +378,21 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
}
result.success(true);
break;
case "requestFocusNodeHref":
result.success(requestFocusNodeHref());
break;
case "requestImageRef":
result.success(requestImageRef());
break;
case "getScrollX":
result.success(getScrollX());
break;
case "getScrollY":
result.success(getScrollY());
break;
case "getCertificate":
result.success(getCertificate());
break;
default:
result.notImplemented();
}
@ -804,14 +821,14 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
result.success(false);
}
public void scrollTo(Integer x, Integer y) {
public void scrollTo(Integer x, Integer y, Boolean animated) {
if (webView != null)
webView.scrollTo(x, y);
webView.scrollTo(x, y, animated);
}
public void scrollBy(Integer x, Integer y) {
public void scrollBy(Integer x, Integer y, Boolean animated) {
if (webView != null)
webView.scrollBy(x, y);
webView.scrollBy(x, y, animated);
}
public void onPauseWebView() {
@ -930,6 +947,36 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
webView.contextMenu = contextMenu;
}
public Map<String, Object> requestFocusNodeHref() {
if (webView != null)
return webView.requestFocusNodeHref();
return null;
}
public Map<String, Object> requestImageRef() {
if (webView != null)
return webView.requestImageRef();
return null;
}
public Integer getScrollX() {
if (webView != null)
return webView.getScrollX();
return null;
}
public Integer getScrollY() {
if (webView != null)
return webView.getScrollY();
return null;
}
public Map<String, Object> getCertificate() {
if (webView != null)
return webView.getSslCertificate();
return null;
}
public void dispose() {
channel.setMethodCallHandler(null);
activityResultListeners.clear();

View File

@ -5,7 +5,6 @@ import android.hardware.display.DisplayManager;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.webkit.ValueCallback;
@ -308,7 +307,8 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
if (webView != null) {
Integer x = (Integer) call.argument("x");
Integer y = (Integer) call.argument("y");
webView.scrollTo(x, y);
Boolean animated = (Boolean) call.argument("animated");
webView.scrollTo(x, y, animated);
}
result.success(true);
break;
@ -316,7 +316,8 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
if (webView != null) {
Integer x = (Integer) call.argument("x");
Integer y = (Integer) call.argument("y");
webView.scrollBy(x, y);
Boolean animated = (Boolean) call.argument("animated");
webView.scrollBy(x, y, animated);
}
result.success(true);
break;
@ -461,14 +462,35 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
if (webView != null) {
result.success(webView.requestFocusNodeHref());
} else {
result.success(false);
result.success(null);
}
break;
case "requestImageRef":
if (webView != null) {
result.success(webView.requestImageRef());
} else {
result.success(false);
result.success(null);
}
break;
case "getScrollX":
if (webView != null) {
result.success(webView.getScrollX());
} else {
result.success(null);
}
break;
case "getScrollY":
if (webView != null) {
result.success(webView.getScrollY());
} else {
result.success(null);
}
break;
case "getCertificate":
if (webView != null) {
result.success(webView.getSslCertificate());
} else {
result.success(null);
}
break;
default:

View File

@ -1,10 +1,13 @@
package com.pichillilorenzo.flutter_inappwebview.InAppWebView;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Point;
import android.net.http.SslCertificate;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@ -36,6 +39,7 @@ import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.Keep;
import androidx.annotation.RequiresApi;
import androidx.webkit.WebViewCompat;
import androidx.webkit.WebViewFeature;
@ -52,6 +56,11 @@ import com.pichillilorenzo.flutter_inappwebview.Util;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -1484,6 +1493,28 @@ final public class InAppWebView extends InputAwareWebView {
channel.invokeMethod("onScrollChanged", obj);
}
public void scrollTo(Integer x, Integer y, Boolean animated) {
if (animated) {
PropertyValuesHolder pvhX = PropertyValuesHolder.ofInt("scrollX", x);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofInt("scrollY", y);
ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(this, pvhX, pvhY);
anim.setDuration(300).start();
} else {
scrollTo(x, y);
}
}
public void scrollBy(Integer x, Integer y, Boolean animated) {
if (animated) {
PropertyValuesHolder pvhX = PropertyValuesHolder.ofInt("scrollX", getScrollX() + x);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofInt("scrollY", getScrollY() + y);
ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(this, pvhX, pvhY);
anim.setDuration(300).start();
} else {
scrollBy(x, y);
}
}
class DownloadStartListener implements DownloadListener {
@Override
public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
@ -1833,6 +1864,110 @@ final public class InAppWebView extends InputAwareWebView {
return obj;
}
public Map<String, Object> getSslCertificate() {
SslCertificate sslCertificate = getCertificate();
SslCertificate.DName issuedByName = sslCertificate.getIssuedBy();
Map<String, Object> issuedBy = new HashMap<>();
issuedBy.put("CName", issuedByName.getCName());
issuedBy.put("DName", issuedByName.getDName());
issuedBy.put("OName", issuedByName.getOName());
issuedBy.put("UName", issuedByName.getUName());
SslCertificate.DName issuedToName = sslCertificate.getIssuedTo();
Map<String, Object> issuedTo = new HashMap<>();
issuedTo.put("CName", issuedToName.getCName());
issuedTo.put("DName", issuedToName.getDName());
issuedTo.put("OName", issuedToName.getOName());
issuedTo.put("UName", issuedToName.getUName());
Map<String, Object> x509CertificateMap = new HashMap<>();
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
X509Certificate x509Certificate = sslCertificate.getX509Certificate();
if (x509Certificate != null) {
x509CertificateMap.put("basicConstraints", x509Certificate.getBasicConstraints());
try {
x509CertificateMap.put("extendedKeyUsage", x509Certificate.getExtendedKeyUsage());
} catch (CertificateParsingException e) {
x509CertificateMap.put("extendedKeyUsage", null);
}
Map<String, Object> issuerDN = new HashMap<>();
issuerDN.put("name", x509Certificate.getIssuerDN().getName());
x509CertificateMap.put("issuerDN", issuerDN);
x509CertificateMap.put("issuerUniqueID", x509Certificate.getIssuerUniqueID());
Map<String, Object> issuerX500Principal = new HashMap<>();
issuerX500Principal.put("name", x509Certificate.getIssuerX500Principal().getName());
issuerX500Principal.put("encoded", x509Certificate.getIssuerX500Principal().getEncoded());
x509CertificateMap.put("issuerX500Principal", issuerX500Principal);
x509CertificateMap.put("keyUsage", x509Certificate.getKeyUsage());
x509CertificateMap.put("notAfter", x509Certificate.getNotAfter().getTime());
x509CertificateMap.put("notBefore", x509Certificate.getNotBefore().getTime());
x509CertificateMap.put("serialNumber", x509Certificate.getSerialNumber().longValue());
x509CertificateMap.put("sigAlgName", x509Certificate.getSigAlgName());
x509CertificateMap.put("sigAlgOID", x509Certificate.getSigAlgOID());
x509CertificateMap.put("sigAlgParams", x509Certificate.getSigAlgParams());
x509CertificateMap.put("signature", x509Certificate.getSignature());
Map<String, Object> subjectDN = new HashMap<>();
subjectDN.put("name", x509Certificate.getSubjectDN().getName());
x509CertificateMap.put("subjectDN", subjectDN);
x509CertificateMap.put("subjectUniqueID", x509Certificate.getSubjectUniqueID());
Map<String, Object> subjectX500Principal = new HashMap<>();
subjectX500Principal.put("name", x509Certificate.getSubjectX500Principal().getName());
subjectX500Principal.put("encoded", x509Certificate.getSubjectX500Principal().getEncoded());
x509CertificateMap.put("subjectX500Principal", subjectX500Principal);
try {
x509CertificateMap.put("TBSCertificate", x509Certificate.getTBSCertificate());
} catch (CertificateEncodingException e) {
x509CertificateMap.put("TBSCertificate", null);
}
x509CertificateMap.put("version", x509Certificate.getVersion());
x509CertificateMap.put("criticalExtensionOIDs", x509Certificate.getCriticalExtensionOIDs());
x509CertificateMap.put("nonCriticalExtensionOIDs", x509Certificate.getNonCriticalExtensionOIDs());
try {
x509CertificateMap.put("encoded", x509Certificate.getEncoded());
} catch (CertificateEncodingException e) {
x509CertificateMap.put("encoded", null);
}
Map<String, Object> publicKey = new HashMap<>();
publicKey.put("algorithm", x509Certificate.getPublicKey().getAlgorithm());
publicKey.put("encoded", x509Certificate.getPublicKey().getEncoded());
publicKey.put("format", x509Certificate.getPublicKey().getFormat());
x509CertificateMap.put("publicKey", publicKey);
x509CertificateMap.put("type", x509Certificate.getType());
x509CertificateMap.put("hasUnsupportedCriticalExtension", x509Certificate.hasUnsupportedCriticalExtension());
try {
x509Certificate.checkValidity();
x509CertificateMap.put("valid", true);
} catch (CertificateExpiredException e) {
x509CertificateMap.put("valid", false);
} catch (CertificateNotYetValidException e) {
x509CertificateMap.put("valid", false);
}
}
}
Map<String, Object> obj = new HashMap<>();
obj.put("issuedBy", issuedBy);
obj.put("issuedTo", issuedTo);
obj.put("validNotAfterDate", sslCertificate.getValidNotAfterDate().getTime());
obj.put("validNotBeforeDate", sslCertificate.getValidNotBeforeDate().getTime());
obj.put("x509Certificate", x509CertificateMap);
return obj;
}
@Override
public void dispose() {
super.dispose();

View File

@ -29,9 +29,11 @@ import com.pichillilorenzo.flutter_inappwebview.CredentialDatabase.Credential;
import com.pichillilorenzo.flutter_inappwebview.CredentialDatabase.CredentialDatabase;
import com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserActivity;
import com.pichillilorenzo.flutter_inappwebview.JavaScriptBridgeInterface;
import com.pichillilorenzo.flutter_inappwebview.Shared;
import com.pichillilorenzo.flutter_inappwebview.Util;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
@ -426,7 +428,8 @@ public class InAppWebViewClient extends WebViewClient {
obj.put("protocol", protocol);
obj.put("realm", realm);
obj.put("port", port);
obj.put("error", error.getPrimaryError());
obj.put("androidError", error.getPrimaryError());
obj.put("iosError", null);
obj.put("serverCertificate", null);
try {
X509Certificate certificate;

View File

@ -6,6 +6,8 @@ import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.webkit.ValueCallback;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
@ -47,7 +49,19 @@ public class MyCookieManager implements MethodChannel.MethodCallHandler {
Long expiresDate = (expiresDateString != null ? new Long(expiresDateString) : null);
Integer maxAge = (Integer) call.argument("maxAge");
Boolean isSecure = (Boolean) call.argument("isSecure");
MyCookieManager.setCookie(url, name, value, domain, path, expiresDate, maxAge, isSecure, result);
Boolean isHttpOnly = (Boolean) call.argument("isHttpOnly");
String sameSite = (String) call.argument("sameSite");
MyCookieManager.setCookie(url,
name,
value,
domain,
path,
expiresDate,
maxAge,
isSecure,
isHttpOnly,
sameSite,
result);
}
break;
case "getCookies":
@ -86,6 +100,8 @@ public class MyCookieManager implements MethodChannel.MethodCallHandler {
Long expiresDate,
Integer maxAge,
Boolean isSecure,
Boolean isHttpOnly,
String sameSite,
final MethodChannel.Result result) {
String cookieValue = name + "=" + value + "; Domain=" + domain + "; Path=" + path;
@ -99,6 +115,12 @@ public class MyCookieManager implements MethodChannel.MethodCallHandler {
if (isSecure != null && isSecure)
cookieValue += "; Secure";
if (isHttpOnly != null && isHttpOnly)
cookieValue += "; HttpOnly";
if (sameSite != null)
cookieValue += "; SameSite=" + sameSite;
cookieValue += ";";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
@ -135,6 +157,14 @@ public class MyCookieManager implements MethodChannel.MethodCallHandler {
Map<String, Object> cookieMap = new HashMap<>();
cookieMap.put("name", name);
cookieMap.put("value", value);
cookieMap.put("expiresDate", null);
cookieMap.put("isSessionOnly", null);
cookieMap.put("domain", null);
cookieMap.put("sameSite", null);
cookieMap.put("isSecure", null);
cookieMap.put("isHttpOnly", null);
cookieMap.put("path", null);
cookieListMap.add(cookieMap);
}
}

View File

@ -1 +1 @@
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.4.4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.9/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.0+hotfix.6/","dependencies":[]}],"android":[{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.4.4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.9/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.0+hotfix.6/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.4+3/","dependencies":[]}],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"e2e","dependencies":[]},{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos"]},{"name":"path_provider_macos","dependencies":[]},{"name":"permission_handler","dependencies":[]}],"date_created":"2020-06-12 02:54:04.283438","version":"1.17.1"}
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.4.4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.9/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.0+hotfix.6/","dependencies":[]}],"android":[{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.4.4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.9/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.0+hotfix.6/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.4+3/","dependencies":[]}],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"e2e","dependencies":[]},{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos"]},{"name":"path_provider_macos","dependencies":[]},{"name":"permission_handler","dependencies":[]}],"date_created":"2020-06-13 01:39:11.912485","version":"1.17.1"}

View File

@ -16,6 +16,7 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
ContextMenu contextMenu;
String url = "";
double progress = 0;
CookieManager _cookieManager = CookieManager.instance();
@override
void initState() {
@ -78,8 +79,8 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
BoxDecoration(border: Border.all(color: Colors.blueAccent)),
child: InAppWebView(
contextMenu: contextMenu,
// initialUrl: "https://github.com/flutter",
initialFile: "assets/index.html",
initialUrl: "https://github.com/flutter",
// initialFile: "assets/index.html",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
@ -101,37 +102,12 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
print("shouldOverrideUrlLoading");
return ShouldOverrideUrlLoadingAction.ALLOW;
},
onCreateWindow: (controller, onCreateWindowRequest) {
print("onCreateWindow");
},
onLoadStop: (InAppWebViewController controller, String url) async {
print("onLoadStop $url");
setState(() {
this.url = url;
});
/*var origins = await WebStorageManager.instance().android.getOrigins();
for (var origin in origins) {
print(origin);
print(await WebStorageManager.instance().android.getQuotaForOrigin(origin: origin.origin));
print(await WebStorageManager.instance().android.getUsageForOrigin(origin: origin.origin));
}
await WebStorageManager.instance().android.deleteAllData();
print("\n\nDELETED\n\n");
origins = await WebStorageManager.instance().android.getOrigins();
for (var origin in origins) {
print(origin);
await WebStorageManager.instance().android.deleteOrigin(origin: origin.origin);
}*/
/*var records = await WebStorageManager.instance().ios.fetchDataRecords(dataTypes: IOSWKWebsiteDataType.ALL);
for(var record in records) {
print(record);
}
await WebStorageManager.instance().ios.removeDataModifiedSince(dataTypes: IOSWKWebsiteDataType.ALL, date: DateTime(0));
print("\n\nDELETED\n\n");
records = await WebStorageManager.instance().ios.fetchDataRecords(dataTypes: IOSWKWebsiteDataType.ALL);
for(var record in records) {
print(record);
}*/
},
onProgressChanged: (InAppWebViewController controller, int progress) {
setState(() {

View File

@ -1,4 +1,4 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">

View File

@ -1,4 +1,4 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">

View File

@ -1,4 +1,4 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">

View File

@ -1,4 +1,4 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">

View File

@ -1,4 +1,4 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">

View File

@ -1,4 +1,4 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">

View File

@ -1,4 +1,4 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">

View File

@ -1,4 +1,4 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">

View File

@ -1,4 +1,4 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">

View File

@ -1,4 +1,4 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">

View File

@ -1,4 +1,4 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">

View File

@ -347,7 +347,8 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor
if webView != nil {
let x = arguments!["x"] as! Int
let y = arguments!["y"] as! Int
webView!.scrollTo(x: x, y: y)
let animated = arguments!["animated"] as! Bool
webView!.scrollTo(x: x, y: y, animated: animated)
}
result(true)
break
@ -355,7 +356,8 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor
if webView != nil {
let x = arguments!["x"] as! Int
let y = arguments!["y"] as! Int
webView!.scrollBy(x: x, y: y)
let animated = arguments!["animated"] as! Bool
webView!.scrollBy(x: x, y: y, animated: animated)
}
result(true)
break
@ -475,6 +477,20 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor
result(false)
}
break
case "getScrollX":
if webView != nil {
result(Int(webView!.scrollView.contentOffset.x))
} else {
result(false)
}
break
case "getScrollY":
if webView != nil {
result(Int(webView!.scrollView.contentOffset.y))
} else {
result(false)
}
break
default:
result(FlutterMethodNotImplemented)
break

View File

@ -243,13 +243,15 @@ public class InAppBrowserWebViewController: UIViewController, FlutterPlugin, UIS
case "scrollTo":
let x = arguments!["x"] as! Int
let y = arguments!["y"] as! Int
webView.scrollTo(x: x, y: y)
let animated = arguments!["animated"] as! Bool
webView.scrollTo(x: x, y: y, animated: animated)
result(true)
break
case "scrollBy":
let x = arguments!["x"] as! Int
let y = arguments!["y"] as! Int
webView.scrollTo(x: x, y: y)
let animated = arguments!["animated"] as! Bool
webView.scrollTo(x: x, y: y, animated: animated)
result(true)
break
case "pauseTimers":

View File

@ -1105,6 +1105,8 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
if #available(iOS 11.0, *) {
accessibilityIgnoresInvertColors = (options?.accessibilityIgnoresInvertColors)!
scrollView.contentInsetAdjustmentBehavior =
UIScrollView.ContentInsetAdjustmentBehavior.init(rawValue: (options?.contentInsetAdjustmentBehavior)!)!
}
configuration.suppressesIncrementalRendering = (options?.suppressesIncrementalRendering)!
@ -1488,6 +1490,10 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
if newOptionsMap["accessibilityIgnoresInvertColors"] != nil && options?.accessibilityIgnoresInvertColors != newOptions.accessibilityIgnoresInvertColors {
accessibilityIgnoresInvertColors = newOptions.accessibilityIgnoresInvertColors
}
if newOptionsMap["contentInsetAdjustmentBehavior"] != nil && options?.contentInsetAdjustmentBehavior != newOptions.contentInsetAdjustmentBehavior {
scrollView.contentInsetAdjustmentBehavior =
UIScrollView.ContentInsetAdjustmentBehavior.init(rawValue: newOptions.contentInsetAdjustmentBehavior)!
}
}
if newOptionsMap["enableViewportScale"] != nil && options?.enableViewportScale != newOptions.enableViewportScale {
@ -2542,6 +2548,10 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
public func onReceivedServerTrustAuthRequest(challenge: URLAuthenticationChallenge, result: FlutterResult?) {
var serverCertificateData: NSData?
let serverTrust = challenge.protectionSpace.serverTrust!
var secResult = SecTrustResultType.invalid
SecTrustEvaluate(serverTrust, &secResult);
if let serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0) {
let serverCertificateCFData = SecCertificateCopyData(serverCertificate)
let data = CFDataGetBytePtr(serverCertificateCFData)
@ -2549,6 +2559,32 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
serverCertificateData = NSData(bytes: data, length: size)
}
let error = secResult != SecTrustResultType.proceed ? secResult.rawValue : nil
var message = ""
switch secResult {
case .deny:
message = "Indicates a user-configured deny; do not proceed."
break
case .fatalTrustFailure:
message = "Indicates a trust failure which cannot be overridden by the user."
break
case .invalid:
message = "Indicates an invalid setting or result."
break
case .otherError:
message = "Indicates a failure other than that of trust evaluation."
break
case .recoverableTrustFailure:
message = "Indicates a trust policy failure which can be overridden by the user."
break
case .unspecified:
message = "Indicates the evaluation succeeded and the certificate is implicitly trusted, but user intent was not explicitly specified."
break
default:
message = ""
}
let arguments: [String: Any?] = [
"host": challenge.protectionSpace.host,
"protocol": challenge.protectionSpace.protocol,
@ -2556,8 +2592,9 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
"port": challenge.protectionSpace.port,
"previousFailureCount": challenge.previousFailureCount,
"serverCertificate": serverCertificateData,
"error": -1,
"message": "",
"androidError": nil,
"iosError": error,
"message": message,
]
channel?.invokeMethod("onReceivedServerTrustAuthRequest", arguments: arguments, result: result)
}
@ -2746,14 +2783,14 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
evaluateJavaScript("wkwebview_ClearMatches();", completionHandler: completionHandler)
}
public func scrollTo(x: Int, y: Int) {
scrollView.setContentOffset(CGPoint(x: x, y: y), animated: false)
public func scrollTo(x: Int, y: Int, animated: Bool) {
scrollView.setContentOffset(CGPoint(x: x, y: y), animated: animated)
}
public func scrollBy(x: Int, y: Int) {
public func scrollBy(x: Int, y: Int, animated: Bool) {
let newX = CGFloat(x) + scrollView.contentOffset.x
let newY = CGFloat(y) + scrollView.contentOffset.y
scrollView.setContentOffset(CGPoint(x: newX, y: newY), animated: false)
scrollView.setContentOffset(CGPoint(x: newX, y: newY), animated: animated)
}

View File

@ -59,6 +59,7 @@ public class InAppWebViewOptions: Options<InAppWebView> {
var isPagingEnabled = false
var maximumZoomScale = 1.0
var minimumZoomScale = 1.0
var contentInsetAdjustmentBehavior = 2 // UIScrollView.ContentInsetAdjustmentBehavior.never
override init(){
super.init()
@ -96,6 +97,7 @@ public class InAppWebViewOptions: Options<InAppWebView> {
realOptions["selectionGranularity"] = configuration.selectionGranularity.rawValue
if #available(iOS 11.0, *) {
realOptions["accessibilityIgnoresInvertColors"] = webView.accessibilityIgnoresInvertColors
realOptions["contentInsetAdjustmentBehavior"] = webView.scrollView.contentInsetAdjustmentBehavior.rawValue
}
realOptions["decelerationRate"] = InAppWebView.getDecelerationRateString(type: webView.scrollView.decelerationRate)
realOptions["alwaysBounceVertical"] = webView.scrollView.alwaysBounceVertical

View File

@ -45,8 +45,20 @@ class MyCookieManager: NSObject, FlutterPlugin {
let maxAge = arguments!["maxAge"] as? Int64
let isSecure = arguments!["isSecure"] as? Bool
let isHttpOnly = arguments!["isHttpOnly"] as? Bool
let sameSite = arguments!["sameSite"] as? String
MyCookieManager.setCookie(url: url, name: name, value: value, domain: domain, path: path, expiresDate: expiresDate, maxAge: maxAge, isSecure: isSecure, result: result)
MyCookieManager.setCookie(url: url,
name: name,
value: value,
domain: domain,
path: path,
expiresDate: expiresDate,
maxAge: maxAge,
isSecure: isSecure,
isHttpOnly: isHttpOnly,
sameSite: sameSite,
result: result)
break
case "getCookies":
let url = arguments!["url"] as! String
@ -82,6 +94,8 @@ class MyCookieManager: NSObject, FlutterPlugin {
expiresDate: Int64?,
maxAge: Int64?,
isSecure: Bool?,
isHttpOnly: Bool?,
sameSite: String?,
result: @escaping FlutterResult) {
var properties: [HTTPCookiePropertyKey: Any] = [:]
properties[.originURL] = url
@ -90,6 +104,7 @@ class MyCookieManager: NSObject, FlutterPlugin {
properties[.domain] = domain
properties[.path] = path
if expiresDate != nil {
// convert from milliseconds
properties[.expires] = Date(timeIntervalSince1970: TimeInterval(Double(expiresDate!)/1000))
}
if maxAge != nil {
@ -98,21 +113,61 @@ class MyCookieManager: NSObject, FlutterPlugin {
if isSecure != nil && isSecure! {
properties[.secure] = "TRUE"
}
if isHttpOnly != nil && isHttpOnly! {
properties[.init("HttpOnly")] = "YES"
}
if sameSite != nil {
if #available(iOS 13.0, *) {
var sameSiteValue = HTTPCookieStringPolicy(rawValue: "None")
switch sameSite {
case "Lax":
sameSiteValue = HTTPCookieStringPolicy.sameSiteLax
case "Strict":
sameSiteValue = HTTPCookieStringPolicy.sameSiteStrict
default:
break
}
properties[.sameSitePolicy] = sameSiteValue
} else {
properties[.init("SameSite")] = sameSite
}
}
let cookie = HTTPCookie(properties: properties)!
MyCookieManager.httpCookieStore!.setCookie(cookie, completionHandler: {() in
result(true)
})
}
public static func getCookies(url: String, result: @escaping FlutterResult) {
var cookieList: [[String: Any]] = []
var cookieList: [[String: Any?]] = []
MyCookieManager.httpCookieStore!.getAllCookies { (cookies) in
for cookie in cookies {
if cookie.domain.contains(URL(string: url)!.host!) {
var sameSite: String? = nil
if #available(iOS 13.0, *) {
if let sameSiteValue = cookie.sameSitePolicy?.rawValue {
sameSite = sameSiteValue.prefix(1).capitalized + sameSiteValue.dropFirst()
}
}
var expiresDateTimestamp: Int64 = -1
if let expiresDate = cookie.expiresDate?.timeIntervalSince1970 {
// convert to milliseconds
expiresDateTimestamp = Int64(expiresDate * 1000)
}
cookieList.append([
"name": cookie.name,
"value": cookie.value
"value": cookie.value,
"expiresDate": expiresDateTimestamp != -1 ? expiresDateTimestamp : nil,
"isSessionOnly": cookie.isSessionOnly,
"domain": cookie.domain,
"sameSite": sameSite,
"isSecure": cookie.isSecure,
"isHttpOnly": cookie.isHTTPOnly,
"path": cookie.path,
])
}
}

View File

@ -40,8 +40,10 @@ class CookieManager {
String path = "/",
int expiresDate,
int maxAge,
bool isSecure}) async {
if (domain == null || domain.isEmpty) domain = _getDomainName(url);
bool isSecure,
bool isHttpOnly,
HTTPCookieSameSitePolicy sameSite}) async {
if (domain == null) domain = _getDomainName(url);
assert(url != null && url.isNotEmpty);
assert(name != null && name.isNotEmpty);
@ -58,6 +60,8 @@ class CookieManager {
args.putIfAbsent('expiresDate', () => expiresDate?.toString());
args.putIfAbsent('maxAge', () => maxAge);
args.putIfAbsent('isSecure', () => isSecure);
args.putIfAbsent('isHttpOnly', () => isHttpOnly);
args.putIfAbsent('sameSite', () => sameSite?.toValue());
await _channel.invokeMethod('setCookie', args);
}
@ -71,10 +75,19 @@ class CookieManager {
List<dynamic> cookieListMap =
await _channel.invokeMethod('getCookies', args);
cookieListMap = cookieListMap.cast<Map<dynamic, dynamic>>();
List<Cookie> cookies = [];
for (var i = 0; i < cookieListMap.length; i++) {
cookies.add(Cookie(
name: cookieListMap[i]["name"], value: cookieListMap[i]["value"]));
name: cookieListMap[i]["name"],
value: cookieListMap[i]["value"],
expiresDate: cookieListMap[i]["expiresDate"],
isSessionOnly: cookieListMap[i]["isSessionOnly"],
domain: cookieListMap[i]["domain"],
sameSite: HTTPCookieSameSitePolicy.fromValue(cookieListMap[i]["sameSite"]),
isSecure: cookieListMap[i]["isSecure"],
isHttpOnly: cookieListMap[i]["isHttpOnly"],
path: cookieListMap[i]["path"]));
}
return cookies;
}
@ -92,7 +105,16 @@ class CookieManager {
for (var i = 0; i < cookies.length; i++) {
cookies[i] = cookies[i].cast<String, dynamic>();
if (cookies[i]["name"] == name)
return Cookie(name: cookies[i]["name"], value: cookies[i]["value"]);
return Cookie(
name: cookies[i]["name"],
value: cookies[i]["value"],
expiresDate: cookies[i]["expiresDate"],
isSessionOnly: cookies[i]["isSessionOnly"],
domain: cookies[i]["domain"],
sameSite: HTTPCookieSameSitePolicy.fromValue(cookies[i]["sameSite"]),
isSecure: cookies[i]["isSecure"],
isHttpOnly: cookies[i]["isHttpOnly"],
path: cookies[i]["path"]);
}
return null;
}

View File

@ -203,36 +203,19 @@ class InAppBrowser {
await _channel.invokeMethod('setOptions', args);
}
///Gets the current [InAppBrowser] options as a `Map`. Returns `null` if the options are not setted yet.
///Gets the current [InAppBrowser] options. Returns `null` if it wasn't able to get them.
Future<InAppBrowserClassOptions> getOptions() async {
this.throwIsNotOpened();
Map<String, dynamic> args = <String, dynamic>{};
InAppBrowserClassOptions inAppBrowserClassOptions =
InAppBrowserClassOptions();
Map<dynamic, dynamic> options =
await _channel.invokeMethod('getOptions', args);
if (options != null) {
options = options.cast<String, dynamic>();
inAppBrowserClassOptions.crossPlatform =
InAppBrowserOptions.fromMap(options);
inAppBrowserClassOptions.inAppWebViewGroupOptions =
InAppWebViewGroupOptions();
inAppBrowserClassOptions.inAppWebViewGroupOptions.crossPlatform =
InAppWebViewOptions.fromMap(options);
if (Platform.isAndroid) {
inAppBrowserClassOptions.android =
AndroidInAppBrowserOptions.fromMap(options);
inAppBrowserClassOptions.inAppWebViewGroupOptions.android =
AndroidInAppWebViewOptions.fromMap(options);
} else if (Platform.isIOS) {
inAppBrowserClassOptions.ios = IOSInAppBrowserOptions.fromMap(options);
inAppBrowserClassOptions.inAppWebViewGroupOptions.ios =
IOSInAppWebViewOptions.fromMap(options);
}
return InAppBrowserClassOptions.fromMap(options);
}
return inAppBrowserClassOptions;
return null;
}
///Returns `true` if the [InAppBrowser] instance is opened, otherwise `false`.

View File

@ -366,14 +366,20 @@ class InAppWebViewController {
String protocol = call.arguments["protocol"];
String realm = call.arguments["realm"];
int port = call.arguments["port"];
int error = call.arguments["error"];
int androidError = call.arguments["androidError"];
int iosError = call.arguments["iosError"];
String message = call.arguments["message"];
Uint8List serverCertificate = call.arguments["serverCertificate"];
AndroidSslError androidSslError = androidError != null ? AndroidSslError.fromValue(androidError) : null;
IOSSslError iosSslError = iosError != null ? IOSSslError.fromValue(iosError) : null;
var protectionSpace = ProtectionSpace(
host: host, protocol: protocol, realm: realm, port: port);
var challenge = ServerTrustChallenge(
protectionSpace: protectionSpace,
error: error,
androidError: androidSslError,
iosError: iosSslError,
message: message,
serverCertificate: serverCertificate);
if (_webview != null &&
@ -811,8 +817,10 @@ class InAppWebViewController {
if (webviewUrl.startsWith("file:///")) {
var assetPathSplitted = webviewUrl.split("/flutter_assets/");
var assetPath = assetPathSplitted[assetPathSplitted.length - 1];
var bytes = await rootBundle.load(assetPath);
html = utf8.decode(bytes.buffer.asUint8List());
try {
var bytes = await rootBundle.load(assetPath);
html = utf8.decode(bytes.buffer.asUint8List());
} catch (e) {}
} else {
HttpClient client = new HttpClient();
var url = Uri.parse(webviewUrl);
@ -839,7 +847,7 @@ class InAppWebViewController {
String manifestUrl;
var html = await getHtml();
if (html != null && html.isEmpty) {
if (html == null || (html != null && html.isEmpty)) {
return favicons;
}
@ -880,8 +888,9 @@ class InAppWebViewController {
var faviconUrl = url.scheme + "://" + url.host + "/favicon.ico";
await client.headUrl(Uri.parse(faviconUrl));
favicons.add(Favicon(url: faviconUrl, rel: "shortcut icon"));
} catch (e) {
} catch (e, stacktrace) {
print("/favicon.ico file not found: " + e.toString());
print(stacktrace);
}
// try to get the manifest file
@ -896,8 +905,9 @@ class InAppWebViewController {
manifestResponse = await manifestRequest.close();
manifestFound = manifestResponse.statusCode == 200 &&
manifestResponse.headers.contentType?.mimeType == "application/json";
} catch (e) {
} catch (e, stacktrace) {
print("Manifest file not found: " + e.toString());
print(stacktrace);
}
if (manifestFound) {
@ -1261,26 +1271,18 @@ class InAppWebViewController {
await _channel.invokeMethod('setOptions', args);
}
///Gets the current WebView options. Returns the options with `null` value if they are not set yet.
///Gets the current WebView options. Returns `null` if it wasn't able to get them.
Future<InAppWebViewGroupOptions> getOptions() async {
Map<String, dynamic> args = <String, dynamic>{};
InAppWebViewGroupOptions inAppWebViewGroupOptions =
InAppWebViewGroupOptions();
Map<dynamic, dynamic> options =
await _channel.invokeMethod('getOptions', args);
if (options != null) {
options = options.cast<String, dynamic>();
inAppWebViewGroupOptions.crossPlatform =
InAppWebViewOptions.fromMap(options);
if (Platform.isAndroid)
inAppWebViewGroupOptions.android =
AndroidInAppWebViewOptions.fromMap(options);
else if (Platform.isIOS)
inAppWebViewGroupOptions.ios = IOSInAppWebViewOptions.fromMap(options);
return InAppWebViewGroupOptions.fromMap(options);
}
return inAppWebViewGroupOptions;
return null;
}
///Gets the WebHistory for this WebView. This contains the back/forward list for use in querying each item in the history stack.
@ -1378,13 +1380,16 @@ class InAppWebViewController {
///
///[y] represents the y position to scroll to.
///
///[animated] `true` to animate the scroll transition, `false` to make the scoll transition immediate.
///
///**Official Android API**: https://developer.android.com/reference/android/view/View#scrollTo(int,%20int)
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiscrollview/1619400-setcontentoffset
Future<void> scrollTo({@required int x, @required int y}) async {
Future<void> scrollTo({@required int x, @required int y, bool animated = false}) async {
assert(x != null && y != null);
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('x', () => x);
args.putIfAbsent('y', () => y);
args.putIfAbsent('animated', () => animated ?? false);
await _channel.invokeMethod('scrollTo', args);
}
@ -1394,13 +1399,16 @@ class InAppWebViewController {
///
///[y] represents the amount of pixels to scroll by vertically.
///
///[animated] `true` to animate the scroll transition, `false` to make the scoll transition immediate.
///
///**Official Android API**: https://developer.android.com/reference/android/view/View#scrollBy(int,%20int)
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiscrollview/1619400-setcontentoffset
Future<void> scrollBy({@required int x, @required int y}) async {
Future<void> scrollBy({@required int x, @required int y, bool animated = false}) async {
assert(x != null && y != null);
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('x', () => x);
args.putIfAbsent('y', () => y);
args.putIfAbsent('animated', () => animated ?? false);
await _channel.invokeMethod('scrollBy', args);
}
@ -1447,13 +1455,15 @@ class InAppWebViewController {
///Performs a zoom operation in this WebView.
///
///[zoomFactor] represents the zoom factor to apply. On Android, the zoom factor will be clamped to the Webview's zoom limits and, also, this value must be in the range 0.01 to 100.0 inclusive.
///[zoomFactor] represents the zoom factor to apply. On Android, the zoom factor will be clamped to the Webview's zoom limits and, also, this value must be in the range 0.01 (excluded) to 100.0 (included).
///
///**NOTE**: available on Android 21+.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#zoomBy(float)
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiscrollview/1619412-setzoomscale
Future<void> zoomBy(double zoomFactor) async {
assert(!Platform.isAndroid || (Platform.isAndroid && zoomFactor > 0.01 && zoomFactor <= 100.0));
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('zoomFactor', () => zoomFactor);
return await _channel.invokeMethod('zoomBy', args);
@ -1627,6 +1637,24 @@ class InAppWebViewController {
return Util.convertColorFromStringRepresentation(colorValue);
}
///Returns the scrolled left position of the current WebView.
///
///**Official Android API**: https://developer.android.com/reference/android/view/View#getScrollX()
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiscrollview/1619404-contentoffset
Future<int> getScrollX() async {
Map<String, dynamic> args = <String, dynamic>{};
return await _channel.invokeMethod('getScrollX', args);
}
///Returns the scrolled top position of the current WebView.
///
///**Official Android API**: https://developer.android.com/reference/android/view/View#getScrollY()
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiscrollview/1619404-contentoffset
Future<int> getScrollY() async {
Map<String, dynamic> args = <String, dynamic>{};
return await _channel.invokeMethod('getScrollY', args);
}
///Gets the default user agent.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebSettings#getDefaultUserAgent(android.content.Context)
@ -1766,6 +1794,16 @@ class AndroidInAppWebViewController {
return await _controller._channel.invokeMethod('clearHistory', args);
}
///Gets the SSL certificate for the main top-level page or null if there is no certificate (the site is not secure).
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#getCertificate()
Future<AndroidSslCertificate> getCertificate() async {
Map<String, dynamic> args = <String, dynamic>{};
var sslCertificateMap = (await _controller._channel.invokeMethod('getCertificate', args))?.cast<String, dynamic>();
return sslCertificateMap != null ? AndroidSslCertificate.fromMap(sslCertificateMap) : null;
}
///Clears the client certificate preferences stored in response to proceeding/cancelling client cert requests.
///Note that WebView automatically clears these preferences when the system keychain is updated.
///The preferences are shared by all the WebViews that are created by the embedder application.

View File

@ -13,6 +13,7 @@ import 'http_auth_credentials_database.dart';
import 'chrome_safari_browser.dart';
import 'cookie_manager.dart';
import 'web_storage_manager.dart';
import 'web_storage.dart';
var uuidGenerator = new Uuid();
@ -912,12 +913,13 @@ class ServerTrustChallenge {
///The protection space requiring authentication.
ProtectionSpace protectionSpace;
///The primary error associated to the server SSL certificate.
///
///**NOTE**: on iOS this value is always -1.
int error;
///Android-specific primary error associated to the server SSL certificate.
AndroidSslError androidError;
///The message associated to the [error].
///iOS-specific primary error associated to the server SSL certificate.
IOSSslError iosError;
///The message associated to the [androidError]/[iosError].
///
///**NOTE**: on iOS this value is always an empty string.
String message;
@ -927,15 +929,17 @@ class ServerTrustChallenge {
ServerTrustChallenge(
{@required this.protectionSpace,
@required this.error,
this.androidError,
this.iosError,
this.message,
this.serverCertificate})
: assert(protectionSpace != null && error != null);
: assert(protectionSpace != null);
Map<String, dynamic> toMap() {
return {
"protectionSpace": protectionSpace?.toMap(),
"error": error,
"androidError": androidError?.toValue(),
"iosError": iosError?.toValue(),
"message": message,
"serverCertificate": serverCertificate
};
@ -1687,6 +1691,18 @@ class InAppWebViewGroupOptions {
return options;
}
static InAppWebViewGroupOptions fromMap(Map<String, dynamic> options) {
InAppWebViewGroupOptions inAppWebViewGroupOptions = InAppWebViewGroupOptions();
inAppWebViewGroupOptions.crossPlatform = InAppWebViewOptions.fromMap(options);
if (Platform.isAndroid)
inAppWebViewGroupOptions.android = AndroidInAppWebViewOptions.fromMap(options);
else if (Platform.isIOS)
inAppWebViewGroupOptions.ios = IOSInAppWebViewOptions.fromMap(options);
return inAppWebViewGroupOptions;
}
Map<String, dynamic> toJson() {
return this.toMap();
}
@ -1695,6 +1711,16 @@ class InAppWebViewGroupOptions {
String toString() {
return toMap().toString();
}
InAppWebViewGroupOptions copy() {
return InAppWebViewGroupOptions.fromMap(this.toMap());
}
InAppWebViewGroupOptions copyWithValue(InAppWebViewGroupOptions webViewOptions) {
var mergedMap = this.toMap();
mergedMap.addAll(webViewOptions.toMap());
return InAppWebViewGroupOptions.fromMap(mergedMap);
}
}
///Class that represents the options that can be used for an [InAppBrowser] WebView.
@ -1741,6 +1767,39 @@ class InAppBrowserClassOptions {
String toString() {
return toMap().toString();
}
static InAppBrowserClassOptions fromMap(Map<String, dynamic> options) {
InAppBrowserClassOptions inAppBrowserClassOptions = InAppBrowserClassOptions();
inAppBrowserClassOptions.crossPlatform =
InAppBrowserOptions.fromMap(options);
inAppBrowserClassOptions.inAppWebViewGroupOptions =
InAppWebViewGroupOptions();
inAppBrowserClassOptions.inAppWebViewGroupOptions.crossPlatform =
InAppWebViewOptions.fromMap(options);
if (Platform.isAndroid) {
inAppBrowserClassOptions.android =
AndroidInAppBrowserOptions.fromMap(options);
inAppBrowserClassOptions.inAppWebViewGroupOptions.android =
AndroidInAppWebViewOptions.fromMap(options);
} else if (Platform.isIOS) {
inAppBrowserClassOptions.ios = IOSInAppBrowserOptions.fromMap(options);
inAppBrowserClassOptions.inAppWebViewGroupOptions.ios =
IOSInAppWebViewOptions.fromMap(options);
}
return inAppBrowserClassOptions;
}
InAppBrowserClassOptions copy() {
return InAppBrowserClassOptions.fromMap(this.toMap());
}
InAppBrowserClassOptions copyWithValue(InAppBrowserClassOptions webViewOptions) {
var mergedMap = this.toMap();
mergedMap.addAll(webViewOptions.toMap());
return InAppBrowserClassOptions.fromMap(mergedMap);
}
}
///Class that represents the options that can be used for an [ChromeSafariBrowser] window.
@ -2445,16 +2504,70 @@ class ContentBlockerActionType {
///Class that represents a cookie returned by the [CookieManager].
class Cookie {
///The name;
///The cookie name.
String name;
///The value;
///The cookie value.
dynamic value;
Cookie({@required this.name, @required this.value});
///The cookie expiration date in milliseconds.
///
///**NOTE**: on Android it will be always `null`.
int expiresDate;
///Indicates if the cookie is a session only cookie.
///
///**NOTE**: on Android it will be always `null`.
bool isSessionOnly;
///The cookie domain.
///
///**NOTE**: on Android it will be always `null`.
String domain;
///The cookie same site policy.
///
///**NOTE**: on Android it will be always `null`.
HTTPCookieSameSitePolicy sameSite;
///Indicates if the cookie is secure or not.
///
///**NOTE**: on Android it will be always `null`.
bool isSecure;
///Indicates if the cookie is a http only cookie.
///
///**NOTE**: on Android it will be always `null`.
bool isHttpOnly;
///The cookie path.
///
///**NOTE**: on Android it will be always `null`.
String path;
Cookie(
{@required this.name,
@required this.value,
this.expiresDate,
this.isSessionOnly,
this.domain,
this.sameSite,
this.isSecure,
this.isHttpOnly,
this.path});
Map<String, dynamic> toMap() {
return {"name": name, "value": value};
return {
"name": name,
"value": value,
"expiresDate": expiresDate,
"isSessionOnly": isSessionOnly,
"domain": domain,
"sameSite": sameSite?.toValue(),
"isSecure": isSecure,
"isHttpOnly": isHttpOnly,
"path": path
};
}
Map<String, dynamic> toJson() {
@ -3263,7 +3376,7 @@ class AndroidWebViewPackageInfo {
return map != null
? AndroidWebViewPackageInfo(
versionName: map["versionName"], packageName: map["packageName"])
: AndroidWebViewPackageInfo();
: null;
}
Map<String, dynamic> toMap() {
@ -3291,17 +3404,10 @@ class RequestFocusNodeHrefResult {
///The image's src attribute.
String src;
RequestFocusNodeHrefResult(
{this.url,
this.title,
this.src});
RequestFocusNodeHrefResult({this.url, this.title, this.src});
Map<String, dynamic> toMap() {
return {
"url": url,
"title": title,
"src": src
};
return {"url": url, "title": title, "src": src};
}
Map<String, dynamic> toJson() {
@ -3341,19 +3447,17 @@ class RequestImageRefResult {
class MetaTag {
///The meta tag name value.
String name;
///The meta tag content value.
String content;
///The meta tag attributes list.
List<MetaTagAttribute> attrs;
MetaTag({this.name, this.content, this.attrs});
Map<String, dynamic> toMap() {
return {
"name": name,
"content": content,
"attrs": attrs
};
return {"name": name, "content": content, "attrs": attrs};
}
Map<String, dynamic> toJson() {
@ -3370,6 +3474,7 @@ class MetaTag {
class MetaTagAttribute {
///The attribute name.
String name;
///The attribute value.
String value;
@ -3392,6 +3497,8 @@ class MetaTagAttribute {
}
}
///Class that represents the type of Web Storage: `localStorage` or `sessionStorage`.
///Used by the [Storage] class.
class WebStorageType {
final String _value;
@ -3411,14 +3518,537 @@ class WebStorageType {
@override
String toString() => _value;
static const LOCAL_STORAGE =
const WebStorageType._internal("localStorage");
///`window.localStorage`: same as [SESSION_STORAGE], but persists even when the browser is closed and reopened.
static const LOCAL_STORAGE = const WebStorageType._internal("localStorage");
///`window.sessionStorage`: maintains a separate storage area for each given origin that's available for the duration
///of the page session (as long as the browser is open, including page reloads and restores).
static const SESSION_STORAGE =
const WebStorageType._internal("sessionStorage");
const WebStorageType._internal("sessionStorage");
bool operator ==(value) => value == _value;
@override
int get hashCode => _value.hashCode;
}
///Class that represents the same site policy of a cookie. Used by the [Cookie] class.
class HTTPCookieSameSitePolicy {
final String _value;
const HTTPCookieSameSitePolicy._internal(this._value);
static HTTPCookieSameSitePolicy fromValue(String value) {
return ([
"Lax",
"Strict",
"None",
].contains(value))
? HTTPCookieSameSitePolicy._internal(value)
: null;
}
String toValue() => _value;
@override
String toString() => _value;
///SameSite=Lax;
///
///Cookies are allowed to be sent with top-level navigations and will be sent along with GET
///request initiated by third party website. This is the default value in modern browsers.
static const LAX = const HTTPCookieSameSitePolicy._internal("Lax");
///SameSite=Strict;
///
///Cookies will only be sent in a first-party context and not be sent along with requests initiated by third party websites.
static const STRICT = const HTTPCookieSameSitePolicy._internal("Strict");
///SameSite=None;
///
///Cookies will be sent in all contexts, i.e sending cross-origin is allowed.
///`None` requires the `Secure` attribute in latest browser versions.
static const NONE = const HTTPCookieSameSitePolicy._internal("None");
bool operator ==(value) => value == _value;
@override
int get hashCode => _value.hashCode;
}
///Class that represents the Android-specific primary error associated to the server SSL certificate.
///Used by the [ServerTrustChallenge] class.
class AndroidSslError {
final int _value;
const AndroidSslError._internal(this._value);
static AndroidSslError fromValue(int value) {
if (value != null && value >= 0 && value <= 5)
return AndroidSslError._internal(value);
return null;
}
int toValue() => _value;
@override
String toString() {
switch (_value) {
case 1:
return "SSL_EXPIRED";
case 2:
return "SSL_IDMISMATCH";
case 3:
return "SSL_UNTRUSTED";
case 4:
return "SSL_DATE_INVALID";
case 5:
return "SSL_INVALID";
case 0:
default:
return "SSL_NOTYETVALID";
}
}
///The certificate is not yet valid
static const SSL_NOTYETVALID = const AndroidSslError._internal(0);
///The certificate has expired
static const SSL_EXPIRED = const AndroidSslError._internal(1);
///Hostname mismatch
static const SSL_IDMISMATCH = const AndroidSslError._internal(2);
///The certificate authority is not trusted
static const SSL_UNTRUSTED = const AndroidSslError._internal(3);
///The date of the certificate is invalid
static const SSL_DATE_INVALID = const AndroidSslError._internal(4);
///A generic error occurred
static const SSL_INVALID = const AndroidSslError._internal(5);
bool operator ==(value) => value == _value;
@override
int get hashCode => _value.hashCode;
}
///Class that represents the iOS-specific primary error associated to the server SSL certificate.
///Used by the [ServerTrustChallenge] class.
class IOSSslError {
final int _value;
const IOSSslError._internal(this._value);
static IOSSslError fromValue(int value) {
if (value != null && [0, 3, 4, 5, 6, 7].contains(value))
return IOSSslError._internal(value);
return null;
}
int toValue() => _value;
@override
String toString() {
switch (_value) {
case 3:
return "DENY";
case 4:
return "UNSPECIFIED";
case 5:
return "RECOVERABLE_TRUST_FAILURE";
case 6:
return "FATAL_TRUST_FAILURE";
case 7:
return "OTHER_ERROR";
case 0:
default:
return "INVALID";
}
}
///Indicates an invalid setting or result.
static const INVALID = const IOSSslError._internal(0);
///Indicates a user-configured deny; do not proceed.
static const DENY = const IOSSslError._internal(3);
///Indicates the evaluation succeeded and the certificate is implicitly trusted, but user intent was not explicitly specified.
static const UNSPECIFIED = const IOSSslError._internal(4);
///Indicates a trust policy failure which can be overridden by the user.
static const RECOVERABLE_TRUST_FAILURE = const IOSSslError._internal(5);
///Indicates a trust failure which cannot be overridden by the user.
static const FATAL_TRUST_FAILURE = const IOSSslError._internal(6);
///Indicates a failure other than that of trust evaluation.
static const OTHER_ERROR = const IOSSslError._internal(7);
bool operator ==(value) => value == _value;
@override
int get hashCode => _value.hashCode;
}
///Class that represents an iOS-specific class used to configure how safe area insets are added to the adjusted content inset.
///
///**NOTE**: available on iOS 11.0+.
class IOSUIScrollViewContentInsetAdjustmentBehavior {
final int _value;
const IOSUIScrollViewContentInsetAdjustmentBehavior._internal(this._value);
static IOSUIScrollViewContentInsetAdjustmentBehavior fromValue(int value) {
if (value != null && value >= 0 && value <= 3)
return IOSUIScrollViewContentInsetAdjustmentBehavior._internal(value);
return null;
}
int toValue() => _value;
@override
String toString() {
switch (_value) {
case 1:
return "SCROLLABLE_AXES";
case 2:
return "NEVER";
case 3:
return "ALWAYS";
case 0:
default:
return "AUTOMATIC";
}
}
///Automatically adjust the scroll view insets.
static const AUTOMATIC = const IOSUIScrollViewContentInsetAdjustmentBehavior._internal(0);
///Adjust the insets only in the scrollable directions.
static const SCROLLABLE_AXES = const IOSUIScrollViewContentInsetAdjustmentBehavior._internal(1);
///Do not adjust the scroll view insets.
static const NEVER = const IOSUIScrollViewContentInsetAdjustmentBehavior._internal(2);
///Always include the safe area insets in the content adjustment.
static const ALWAYS = const IOSUIScrollViewContentInsetAdjustmentBehavior._internal(3);
bool operator ==(value) => value == _value;
@override
int get hashCode => _value.hashCode;
}
class AndroidSslCertificate {
AndroidSslCertificateDName issuedBy;
AndroidSslCertificateDName issuedTo;
int validNotAfterDate;
int validNotBeforeDate;
AndroidX509Certificate x509Certificate;
AndroidSslCertificate({
this.issuedBy,
this.issuedTo,
this.validNotAfterDate,
this.validNotBeforeDate,
this.x509Certificate
});
static AndroidSslCertificate fromMap(Map<String, dynamic> map) {
return map != null ? AndroidSslCertificate(
issuedBy: AndroidSslCertificateDName.fromMap(map["issuedBy"]?.cast<String, dynamic>()),
issuedTo: AndroidSslCertificateDName.fromMap(map["issuedTo"]?.cast<String, dynamic>()),
validNotAfterDate: map["validNotAfterDate"],
validNotBeforeDate: map["validNotBeforeDate"],
x509Certificate: AndroidX509Certificate.fromMap(map["x509Certificate"]?.cast<String, dynamic>()),
) : null;
}
Map<String, dynamic> toMap() {
return {
"issuedBy": issuedBy?.toMap(),
"issuedTo": issuedTo?.toMap(),
"validNotAfterDate": validNotAfterDate,
"validNotBeforeDate": validNotBeforeDate,
"x509Certificate": x509Certificate?.toMap(),
};
}
Map<String, dynamic> toJson() {
return this.toMap();
}
@override
String toString() {
return toMap().toString();
}
}
class AndroidSslCertificateDName {
// ignore: non_constant_identifier_names
String CName;
// ignore: non_constant_identifier_names
String DName;
// ignore: non_constant_identifier_names
String OName;
// ignore: non_constant_identifier_names
String UName;
// ignore: non_constant_identifier_names
AndroidSslCertificateDName({this.CName, this.DName, this.OName, this.UName});
static AndroidSslCertificateDName fromMap(Map<String, dynamic> map) {
return map != null ? AndroidSslCertificateDName(
CName: map["CName"],
DName: map["DName"],
OName: map["OName"],
UName: map["UName"],
) : null;
}
Map<String, dynamic> toMap() {
return {
"CName": CName,
"DName": DName,
"OName": OName,
"UName": UName,
};
}
Map<String, dynamic> toJson() {
return this.toMap();
}
@override
String toString() {
return toMap().toString();
}
}
class AndroidX509Certificate {
int basicConstraints;
List<String> extendedKeyUsage;
AndroidX509CertificatePrincipal issuerDN;
List<bool> issuerUniqueID;
AndroidX500Principal issuerX500Principal;
List<bool> keyUsage;
int notAfter;
int notBefore;
int serialNumber;
String sigAlgName;
String sigAlgOID;
Uint8List sigAlgParams;
Uint8List signature;
AndroidX509CertificatePrincipal subjectDN;
List<bool> subjectUniqueID;
AndroidX500Principal subjectX500Principal;
// ignore: non_constant_identifier_names
Uint8List TBSCertificate;
int version;
Set<String> criticalExtensionOIDs;
Set<String> nonCriticalExtensionOIDs;
Uint8List encoded;
AndroidX509CertificatePublicKey publicKey;
String type;
bool hasUnsupportedCriticalExtension;
bool valid;
AndroidX509Certificate({
this.basicConstraints,
this.extendedKeyUsage,
this.issuerDN,
this.issuerUniqueID,
this.issuerX500Principal,
this.keyUsage,
this.notAfter,
this.notBefore,
this.serialNumber,
this.sigAlgName,
this.sigAlgOID,
this.sigAlgParams,
this.signature,
this.subjectDN,
this.subjectUniqueID,
this.subjectX500Principal,
// ignore: non_constant_identifier_names
this.TBSCertificate,
this.version,
this.criticalExtensionOIDs,
this.nonCriticalExtensionOIDs,
this.encoded,
this.publicKey,
this.type,
this.hasUnsupportedCriticalExtension,
this.valid
});
static AndroidX509Certificate fromMap(Map<String, dynamic> map) {
return map != null ? AndroidX509Certificate(
basicConstraints: map["basicConstraints"],
extendedKeyUsage: map["extendedKeyUsage"],
issuerDN: AndroidX509CertificatePrincipal.fromMap(map["issuerDN"]),
issuerUniqueID: map["issuerUniqueID"],
issuerX500Principal: AndroidX500Principal.fromMap(map["issuerX500Principal"]),
keyUsage: map["keyUsage"],
notAfter: map["notAfter"],
notBefore: map["notBefore"],
serialNumber: map["serialNumber"],
sigAlgName: map["sigAlgName"],
sigAlgOID: map["sigAlgOID"],
sigAlgParams: map["sigAlgParams"],
signature: map["signature"],
subjectDN: AndroidX509CertificatePrincipal.fromMap(map["subjectDN"]),
subjectUniqueID: map["subjectUniqueID"],
subjectX500Principal: AndroidX500Principal.fromMap(map["subjectX500Principal"]),
TBSCertificate: map["TBSCertificate"],
version: map["version"],
criticalExtensionOIDs: map["criticalExtensionOIDs"],
nonCriticalExtensionOIDs: map["nonCriticalExtensionOIDs"],
encoded: map["encoded"],
publicKey: AndroidX509CertificatePublicKey.fromMap(map["publicKey"]),
type: map["type"],
hasUnsupportedCriticalExtension: map["hasUnsupportedCriticalExtension"],
valid: map["valid"]
) : null;
}
Map<String, dynamic> toMap() {
return {
"basicConstraints": basicConstraints,
"extendedKeyUsage": extendedKeyUsage,
"issuerDN": issuerDN?.toMap(),
"issuerUniqueID": issuerUniqueID,
"issuerX500Principal": issuerX500Principal?.toMap(),
"keyUsage": keyUsage,
"notAfter": notAfter,
"notBefore": notBefore,
"serialNumber": serialNumber,
"sigAlgName": sigAlgName,
"sigAlgOID": sigAlgOID,
"sigAlgParams": sigAlgParams,
"signature": signature,
"subjectDN": subjectDN?.toMap(),
"subjectUniqueID": subjectUniqueID,
"subjectX500Principal": subjectX500Principal?.toMap(),
"TBSCertificate": TBSCertificate,
"version": version,
"criticalExtensionOIDs": criticalExtensionOIDs,
"nonCriticalExtensionOIDs": nonCriticalExtensionOIDs,
"encoded": encoded,
"publicKey": publicKey?.toMap(),
"type": type,
"hasUnsupportedCriticalExtension": hasUnsupportedCriticalExtension,
"valid": valid,
};
}
Map<String, dynamic> toJson() {
return this.toMap();
}
@override
String toString() {
return toMap().toString();
}
}
class AndroidX509CertificatePrincipal {
String name;
AndroidX509CertificatePrincipal({
this.name
});
static AndroidX509CertificatePrincipal fromMap(Map<String, dynamic> map) {
return map != null ? AndroidX509CertificatePrincipal(
name: map["name"],
) : null;
}
Map<String, dynamic> toMap() {
return {
"name": name,
};
}
Map<String, dynamic> toJson() {
return this.toMap();
}
@override
String toString() {
return toMap().toString();
}
}
class AndroidX500Principal {
String name;
Uint8List encoded;
AndroidX500Principal({
this.name,
this.encoded
});
static AndroidX500Principal fromMap(Map<String, dynamic> map) {
return map != null ? AndroidX500Principal(
name: map["name"],
encoded: map["encoded"],
) : null;
}
Map<String, dynamic> toMap() {
return {
"name": name,
"encoded": encoded,
};
}
Map<String, dynamic> toJson() {
return this.toMap();
}
@override
String toString() {
return toMap().toString();
}
}
class AndroidX509CertificatePublicKey {
String algorithm;
Uint8List encoded;
String format;
AndroidX509CertificatePublicKey({
this.algorithm,
this.encoded,
this.format
});
static AndroidX509CertificatePublicKey fromMap(Map<String, dynamic> map) {
return map != null ? AndroidX509CertificatePublicKey(
algorithm: map["algorithm"],
encoded: map["encoded"],
format: map["format"],
) : null;
}
Map<String, dynamic> toMap() {
return {
"algorithm": algorithm,
"encoded": encoded,
"format": format,
};
}
Map<String, dynamic> toJson() {
return this.toMap();
}
@override
String toString() {
return toMap().toString();
}
}

View File

@ -17,6 +17,10 @@ class WebViewOptions {
return null;
}
WebViewOptions copy() {
return WebViewOptions.fromMap(this.toMap());
}
Map<String, dynamic> toJson() {
return this.toMap();
}
@ -36,6 +40,10 @@ class BrowserOptions {
return null;
}
BrowserOptions copy() {
return BrowserOptions.fromMap(this.toMap());
}
Map<String, dynamic> toJson() {
return this.toMap();
}
@ -55,6 +63,10 @@ class ChromeSafariBrowserOptions {
return null;
}
ChromeSafariBrowserOptions copy() {
return ChromeSafariBrowserOptions.fromMap(this.toMap());
}
Map<String, dynamic> toJson() {
return this.toMap();
}
@ -284,6 +296,17 @@ class InAppWebViewOptions
String toString() {
return toMap().toString();
}
@override
InAppWebViewOptions copy() {
return InAppWebViewOptions.fromMap(this.toMap());
}
InAppWebViewOptions copyWithValue(InAppWebViewOptions webViewOptions) {
var mergedMap = this.toMap();
mergedMap.addAll(webViewOptions.toMap());
return InAppWebViewOptions.fromMap(mergedMap);
}
}
///This class represents all the Android-only WebView options available.
@ -681,6 +704,17 @@ class AndroidInAppWebViewOptions
String toString() {
return toMap().toString();
}
@override
AndroidInAppWebViewOptions copy() {
return AndroidInAppWebViewOptions.fromMap(this.toMap());
}
AndroidInAppWebViewOptions copyWithValue(AndroidInAppWebViewOptions webViewOptions) {
var mergedMap = this.toMap();
mergedMap.addAll(webViewOptions.toMap());
return AndroidInAppWebViewOptions.fromMap(mergedMap);
}
}
///This class represents all the iOS-only WebView options available.
@ -789,6 +823,10 @@ class IOSInAppWebViewOptions
///The default value is `1.0`.
double minimumZoomScale;
///Configures how safe area insets are added to the adjusted content inset.
///The default value is [IOSUIScrollViewContentInsetAdjustmentBehavior.NEVER].
IOSUIScrollViewContentInsetAdjustmentBehavior contentInsetAdjustmentBehavior;
IOSInAppWebViewOptions(
{this.disallowOverScroll = false,
this.enableViewportScale = false,
@ -811,7 +849,8 @@ class IOSInAppWebViewOptions
this.scrollsToTop = true,
this.isPagingEnabled = false,
this.maximumZoomScale = 1.0,
this.minimumZoomScale = 1.0});
this.minimumZoomScale = 1.0,
this.contentInsetAdjustmentBehavior = IOSUIScrollViewContentInsetAdjustmentBehavior.NEVER});
@override
Map<String, dynamic> toMap() {
@ -845,7 +884,8 @@ class IOSInAppWebViewOptions
"scrollsToTop": scrollsToTop,
"isPagingEnabled": isPagingEnabled,
"maximumZoomScale": maximumZoomScale,
"minimumZoomScale": minimumZoomScale
"minimumZoomScale": minimumZoomScale,
"contentInsetAdjustmentBehavior": contentInsetAdjustmentBehavior.toValue()
};
}
@ -889,6 +929,9 @@ class IOSInAppWebViewOptions
options.isPagingEnabled = map["isPagingEnabled"];
options.maximumZoomScale = map["maximumZoomScale"];
options.minimumZoomScale = map["minimumZoomScale"];
options.contentInsetAdjustmentBehavior =
IOSUIScrollViewContentInsetAdjustmentBehavior.fromValue(
map["contentInsetAdjustmentBehavior"]);
return options;
}
@ -901,6 +944,17 @@ class IOSInAppWebViewOptions
String toString() {
return toMap().toString();
}
@override
IOSInAppWebViewOptions copy() {
return IOSInAppWebViewOptions.fromMap(this.toMap());
}
IOSInAppWebViewOptions copyWithValue(IOSInAppWebViewOptions webViewOptions) {
var mergedMap = this.toMap();
mergedMap.addAll(webViewOptions.toMap());
return IOSInAppWebViewOptions.fromMap(mergedMap);
}
}
///This class represents all the cross-platform [InAppBrowser] options available.
@ -953,6 +1007,17 @@ class InAppBrowserOptions
String toString() {
return toMap().toString();
}
@override
InAppBrowserOptions copy() {
return InAppBrowserOptions.fromMap(this.toMap());
}
InAppBrowserOptions copyWithValue(InAppBrowserOptions webViewOptions) {
var mergedMap = this.toMap();
mergedMap.addAll(webViewOptions.toMap());
return InAppBrowserOptions.fromMap(mergedMap);
}
}
///This class represents all the Android-only [InAppBrowser] options available.
@ -1003,6 +1068,17 @@ class AndroidInAppBrowserOptions implements BrowserOptions, AndroidOptions {
String toString() {
return toMap().toString();
}
@override
AndroidInAppBrowserOptions copy() {
return AndroidInAppBrowserOptions.fromMap(this.toMap());
}
AndroidInAppBrowserOptions copyWithValue(AndroidInAppBrowserOptions webViewOptions) {
var mergedMap = this.toMap();
mergedMap.addAll(webViewOptions.toMap());
return AndroidInAppBrowserOptions.fromMap(mergedMap);
}
}
///This class represents all the iOS-only [InAppBrowser] options available.
@ -1079,6 +1155,17 @@ class IOSInAppBrowserOptions implements BrowserOptions, IosOptions {
String toString() {
return toMap().toString();
}
@override
IOSInAppBrowserOptions copy() {
return IOSInAppBrowserOptions.fromMap(this.toMap());
}
IOSInAppBrowserOptions copyWithValue(IOSInAppBrowserOptions webViewOptions) {
var mergedMap = this.toMap();
mergedMap.addAll(webViewOptions.toMap());
return IOSInAppBrowserOptions.fromMap(mergedMap);
}
}
///This class represents all the Android-only [ChromeSafariBrowser] options available.
@ -1153,6 +1240,17 @@ class AndroidChromeCustomTabsOptions
String toString() {
return toMap().toString();
}
@override
AndroidChromeCustomTabsOptions copy() {
return AndroidChromeCustomTabsOptions.fromMap(this.toMap());
}
AndroidChromeCustomTabsOptions copyWithValue(AndroidChromeCustomTabsOptions webViewOptions) {
var mergedMap = this.toMap();
mergedMap.addAll(webViewOptions.toMap());
return AndroidChromeCustomTabsOptions.fromMap(mergedMap);
}
}
///This class represents all the iOS-only [ChromeSafariBrowser] options available.
@ -1230,4 +1328,15 @@ class IOSSafariOptions implements ChromeSafariBrowserOptions, IosOptions {
String toString() {
return toMap().toString();
}
@override
IOSSafariOptions copy() {
return IOSSafariOptions.fromMap(this.toMap());
}
IOSSafariOptions copyWithValue(IOSSafariOptions webViewOptions) {
var mergedMap = this.toMap();
mergedMap.addAll(webViewOptions.toMap());
return IOSSafariOptions.fromMap(mergedMap);
}
}