Added InAppWebViewSettings.allowBackgroundAudioPlaying for Android, Added WebViewAssetLoader and InAppWebViewSettings.webViewAssetLoader for Android

This commit is contained in:
Lorenzo Pichilli 2022-10-22 04:05:41 +02:00
parent b6e7699ef8
commit 14ff4921f8
55 changed files with 1107 additions and 528 deletions

View File

@ -1,3 +1,12 @@
## 6.0.0-beta.6
- Added `InAppWebViewSettings.allowBackgroundAudioPlaying` for Android
- Added `WebViewAssetLoader` and `InAppWebViewSettings.webViewAssetLoader` for Android
### BREAKING CHANGES
- `WebResourceResponse.contentType` and `WebResourceResponse.contentEncoding` properties can be null
## 6.0.0-beta.5
- Merge fixes of version `5.5.0+5`
@ -54,6 +63,12 @@
- Removed `URLProtectionSpace.iosIsProxy` property
- `historyUrl` and `baseUrl` of `InAppWebViewInitialData` can be `null`
## 5.6.0
- Fixed "URLCredential.fromMap returns null for username" [#1205](https://github.com/pichillilorenzo/flutter_inappwebview/issues/1205)
- Fixed "Compare to webview_flutter, inappwebview is significant frame dropped while page scrolling" [#1386](https://github.com/pichillilorenzo/flutter_inappwebview/issues/1386)
- Merged "Fix hybrid composition laggy" [#1387](https://github.com/pichillilorenzo/flutter_inappwebview/pull/1387) (thanks to [Doflatango](https://github.com/Doflatango))
## 5.5.0+5
- Fixed `HeadlessInAppWebView` default size on Android

View File

@ -2,7 +2,6 @@ package com.pichillilorenzo.flutter_inappwebview.service_worker;
import android.annotation.SuppressLint;
import android.os.Build;
import android.webkit.ServiceWorkerController;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -12,10 +11,8 @@ import androidx.webkit.ServiceWorkerWebSettingsCompat;
import androidx.webkit.WebViewFeature;
import com.pichillilorenzo.flutter_inappwebview.Util;
import com.pichillilorenzo.flutter_inappwebview.headless_in_app_webview.HeadlessInAppWebView;
import com.pichillilorenzo.flutter_inappwebview.types.BaseCallbackResultImpl;
import com.pichillilorenzo.flutter_inappwebview.types.ChannelDelegateImpl;
import com.pichillilorenzo.flutter_inappwebview.types.Disposable;
import com.pichillilorenzo.flutter_inappwebview.types.SyncBaseCallbackResultImpl;
import com.pichillilorenzo.flutter_inappwebview.types.WebResourceRequestExt;
import com.pichillilorenzo.flutter_inappwebview.types.WebResourceResponseExt;
@ -136,7 +133,7 @@ public class ServiceWorkerChannelDelegate extends ChannelDelegateImpl {
}
}
public void shouldInterceptRequest(WebResourceRequestExt request, @NonNull WebViewChannelDelegate.ShouldInterceptRequestCallback callback) {
public void shouldInterceptRequest(WebResourceRequestExt request, @NonNull ShouldInterceptRequestCallback callback) {
MethodChannel channel = getChannel();
if (channel == null) return;
channel.invokeMethod("shouldInterceptRequest", request.toMap(), callback);
@ -146,7 +143,7 @@ public class ServiceWorkerChannelDelegate extends ChannelDelegateImpl {
@Nullable
@Override
public WebResourceResponseExt decodeResult(@Nullable Object obj) {
return (new WebViewChannelDelegate.ShouldInterceptRequestCallback()).decodeResult(obj);
return (new ShouldInterceptRequestCallback()).decodeResult(obj);
}
}
@ -154,7 +151,7 @@ public class ServiceWorkerChannelDelegate extends ChannelDelegateImpl {
public WebResourceResponseExt shouldInterceptRequest(WebResourceRequestExt request) throws InterruptedException {
MethodChannel channel = getChannel();
if (channel == null) return null;
final WebViewChannelDelegate.SyncShouldInterceptRequestCallback callback = new WebViewChannelDelegate.SyncShouldInterceptRequestCallback();
final SyncShouldInterceptRequestCallback callback = new SyncShouldInterceptRequestCallback();
return Util.invokeMethodAndWaitResult(channel, "shouldInterceptRequest", request.toMap(), callback);
}

View File

@ -1,7 +1,6 @@
package com.pichillilorenzo.flutter_inappwebview.types;
import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.concurrent.CountDownLatch;

View File

@ -13,7 +13,9 @@ import java.util.HashMap;
import java.util.Map;
public class WebResourceResponseExt {
@Nullable
private String contentType;
@Nullable
private String contentEncoding;
@Nullable
private Integer statusCode;
@ -24,7 +26,7 @@ public class WebResourceResponseExt {
@Nullable
private byte[] data;
public WebResourceResponseExt(String contentType, String contentEncoding, @Nullable Integer statusCode,
public WebResourceResponseExt(@Nullable String contentType, @Nullable String contentEncoding, @Nullable Integer statusCode,
@Nullable String reasonPhrase, @Nullable Map<String, String> headers, @Nullable byte[] data) {
this.contentType = contentType;
this.contentEncoding = contentEncoding;
@ -77,19 +79,21 @@ public class WebResourceResponseExt {
return webResourceResponseMap;
}
@Nullable
public String getContentType() {
return contentType;
}
public void setContentType(String contentType) {
public void setContentType(@Nullable String contentType) {
this.contentType = contentType;
}
@Nullable
public String getContentEncoding() {
return contentEncoding;
}
public void setContentEncoding(String contentEncoding) {
public void setContentEncoding(@Nullable String contentEncoding) {
this.contentEncoding = contentEncoding;
}

View File

@ -0,0 +1,209 @@
package com.pichillilorenzo.flutter_inappwebview.types;
import android.content.Context;
import android.os.Build;
import android.util.Log;
import android.webkit.WebResourceResponse;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.webkit.WebViewAssetLoader;
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
import com.pichillilorenzo.flutter_inappwebview.Util;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.flutter.plugin.common.MethodChannel;
public class WebViewAssetLoaderExt implements Disposable {
@Nullable
public WebViewAssetLoader loader;
@NonNull
public List<PathHandlerExt> customPathHandlers;
public WebViewAssetLoaderExt(@Nullable WebViewAssetLoader loader, @NonNull List<PathHandlerExt> customPathHandlers) {
this.loader = loader;
this.customPathHandlers = customPathHandlers;
}
@Nullable
public static WebViewAssetLoaderExt fromMap(@Nullable Map<String, Object> map, @NonNull InAppWebViewFlutterPlugin plugin, @NonNull Context context) {
if (map == null) {
return null;
}
WebViewAssetLoader.Builder builder = new WebViewAssetLoader.Builder();
String domain = (String) map.get("domain");
Boolean httpAllowed = (Boolean) map.get("httpAllowed");
List<Map<String, Object>> pathHandlers = (List<Map<String, Object>>) map.get("pathHandlers");
List<PathHandlerExt> customPathHandlers = new ArrayList<>();
if (domain != null && !domain.isEmpty()) {
builder.setDomain(domain);
}
if (httpAllowed != null) {
builder.setHttpAllowed(httpAllowed);
}
if (pathHandlers != null) {
for (Map<String, Object> pathHandler : pathHandlers) {
String type = (String) pathHandler.get("type");
String path = (String) pathHandler.get("path");
if (type == null || path == null) {
continue;
}
switch (type) {
case "AssetsPathHandler":
WebViewAssetLoader.AssetsPathHandler assetsPathHandler =
new WebViewAssetLoader.AssetsPathHandler(context);
builder.addPathHandler(path, assetsPathHandler);
break;
case "InternalStoragePathHandler":
String directory = (String) pathHandler.get("directory");
if (directory == null) {
continue;
}
File dir = new File(directory);
WebViewAssetLoader.InternalStoragePathHandler internalStoragePathHandler =
new WebViewAssetLoader.InternalStoragePathHandler(context, dir);
builder.addPathHandler(path, internalStoragePathHandler);
break;
case "ResourcesPathHandler":
WebViewAssetLoader.ResourcesPathHandler resourcesPathHandler = new WebViewAssetLoader.ResourcesPathHandler(context);
builder.addPathHandler(path, resourcesPathHandler);
break;
default:
String id = (String) pathHandler.get("id");
if (id == null) {
continue;
}
PathHandlerExt customPathHandler = new PathHandlerExt(id, plugin);
builder.addPathHandler(path, customPathHandler);
customPathHandlers.add(customPathHandler);
break;
}
}
}
return new WebViewAssetLoaderExt(builder.build(), customPathHandlers);
}
@Override
public void dispose() {
for (PathHandlerExt pathHandler : customPathHandlers) {
pathHandler.dispose();
}
customPathHandlers.clear();
}
public static class PathHandlerExt implements WebViewAssetLoader.PathHandler, Disposable {
protected static final String LOG_TAG = "PathHandlerExt";
public static final String METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappwebview_custompathhandler_";
@NonNull
public String id;
@Nullable
public PathHandlerExtChannelDelegate channelDelegate;
public PathHandlerExt(@NonNull String id, @NonNull InAppWebViewFlutterPlugin plugin) {
this.id = id;
final MethodChannel channel = new MethodChannel(plugin.messenger, METHOD_CHANNEL_NAME_PREFIX + id);
this.channelDelegate = new PathHandlerExtChannelDelegate(this, channel);
}
@Nullable
@Override
public WebResourceResponse handle(@NonNull String path) {
if (channelDelegate != null) {
WebResourceResponseExt response = null;
try {
response = channelDelegate.handle(path);
} catch (InterruptedException e) {
e.printStackTrace();
return null;
}
if (response != null) {
String contentType = response.getContentType();
String contentEncoding = response.getContentEncoding();
byte[] data = response.getData();
Map<String, String> responseHeaders = response.getHeaders();
Integer statusCode = response.getStatusCode();
String reasonPhrase = response.getReasonPhrase();
ByteArrayInputStream inputStream = (data != null) ? new ByteArrayInputStream(data) : null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && statusCode != null && reasonPhrase != null) {
return new WebResourceResponse(contentType, contentEncoding, statusCode, reasonPhrase, responseHeaders, inputStream);
} else {
return new WebResourceResponse(contentType, contentEncoding, inputStream);
}
}
}
return null;
}
@Override
public void dispose() {
if (channelDelegate != null) {
channelDelegate.dispose();
channelDelegate = null;
}
}
}
public static class PathHandlerExtChannelDelegate extends ChannelDelegateImpl {
@Nullable
private PathHandlerExt pathHandler;
public PathHandlerExtChannelDelegate(@NonNull PathHandlerExt pathHandler, @NonNull MethodChannel channel) {
super(channel);
this.pathHandler = pathHandler;
}
public static class HandleCallback extends BaseCallbackResultImpl<WebResourceResponseExt> {
@Nullable
@Override
public WebResourceResponseExt decodeResult(@Nullable Object obj) {
return WebResourceResponseExt.fromMap((Map<String, Object>) obj);
}
}
public void handle(String path, @NonNull HandleCallback callback) {
MethodChannel channel = getChannel();
if (channel == null) return;
Map<String, Object> obj = new HashMap<>();
obj.put("path", path);
channel.invokeMethod("handle", obj, callback);
}
public static class SyncHandleCallback extends SyncBaseCallbackResultImpl<WebResourceResponseExt> {
@Nullable
@Override
public WebResourceResponseExt decodeResult(@Nullable Object obj) {
return (new HandleCallback()).decodeResult(obj);
}
}
@Nullable
public WebResourceResponseExt handle(String path) throws InterruptedException {
MethodChannel channel = getChannel();
if (channel == null) return null;
final SyncHandleCallback callback = new SyncHandleCallback();
Map<String, Object> obj = new HashMap<>();
obj.put("path", path);
return Util.invokeMethodAndWaitResult(channel, "handle", obj, callback);
}
@Override
public void dispose() {
super.dispose();
pathHandler = null;
}
}
}

View File

@ -88,6 +88,7 @@ import com.pichillilorenzo.flutter_inappwebview.types.PreferredContentModeOption
import com.pichillilorenzo.flutter_inappwebview.types.URLRequest;
import com.pichillilorenzo.flutter_inappwebview.types.UserContentController;
import com.pichillilorenzo.flutter_inappwebview.types.UserScript;
import com.pichillilorenzo.flutter_inappwebview.types.WebViewAssetLoaderExt;
import com.pichillilorenzo.flutter_inappwebview.webview.ContextMenuSettings;
import com.pichillilorenzo.flutter_inappwebview.webview.InAppWebViewInterface;
import com.pichillilorenzo.flutter_inappwebview.webview.JavaScriptBridgeInterface;
@ -108,7 +109,6 @@ import java.util.UUID;
import java.util.regex.Pattern;
import io.flutter.plugin.common.MethodChannel;
import okhttp3.OkHttpClient;
final public class InAppWebView extends InputAwareWebView implements InAppWebViewInterface {
protected static final String LOG_TAG = "InAppWebView";
@ -134,9 +134,7 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie
public InAppWebViewSettings customSettings = new InAppWebViewSettings();
public boolean isLoading = false;
private boolean inFullscreen = false;
public OkHttpClient httpClient;
public float zoomScale = 1.0f;
int okHttpClientCacheSize = 10 * 1024 * 1024; // 10MB
public ContentBlockerHandler contentBlockerHandler = new ContentBlockerHandler();
public Pattern regexToCancelSubFramesLoadingCompiled;
@Nullable
@ -168,6 +166,9 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie
@Nullable
public FindInteractionController findInteractionController;
@Nullable
public WebViewAssetLoaderExt webViewAssetLoaderExt;
public InAppWebView(Context context) {
super(context);
}
@ -200,7 +201,9 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie
@SuppressLint("RestrictedApi")
public void prepare() {
httpClient = new OkHttpClient().newBuilder().build();
if (plugin != null) {
webViewAssetLoaderExt = WebViewAssetLoaderExt.fromMap(customSettings.webViewAssetLoader, plugin, getContext());
}
javaScriptBridgeInterface = new JavaScriptBridgeInterface(this);
addJavascriptInterface(javaScriptBridgeInterface, JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME);
@ -1052,6 +1055,13 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie
WebSettingsCompat.setEnterpriseAuthenticationAppLinkPolicyEnabled(settings, newCustomSettings.enterpriseAuthenticationAppLinkPolicyEnabled);
}
if (plugin != null) {
if (webViewAssetLoaderExt != null) {
webViewAssetLoaderExt.dispose();
}
webViewAssetLoaderExt = WebViewAssetLoaderExt.fromMap(customSettings.webViewAssetLoader, plugin, getContext());
}
customSettings = newCustomSettings;
}
@ -1848,10 +1858,16 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie
throw new UnsupportedOperationException();
}
// @Override
// protected void onWindowVisibilityChanged(int visibility) {
// if (visibility != View.GONE) super.onWindowVisibilityChanged(View.VISIBLE);
// }
@Override
protected void onWindowVisibilityChanged(int visibility) {
if (customSettings.allowBackgroundAudioPlaying) {
if (visibility != View.GONE) {
super.onWindowVisibilityChanged(View.VISIBLE);
}
return;
}
super.onWindowVisibilityChanged(visibility);
}
public float getZoomScale() {
return zoomScale;
@ -1946,6 +1962,10 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie
findInteractionController.dispose();
findInteractionController = null;
}
if (webViewAssetLoaderExt != null) {
webViewAssetLoaderExt.dispose();
webViewAssetLoaderExt = null;
}
if (windowId != null) {
InAppWebViewChromeClient.windowWebViewMessages.remove(windowId);
}

View File

@ -99,7 +99,9 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_FULLSCREEN;
@Nullable
private View mCustomView;
@Nullable
private WebChromeClient.CustomViewCallback mCustomViewCallback;
private int mOriginalOrientation;
private int mOriginalSystemUiVisibility;
@ -140,11 +142,15 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
if (decorView == null) {
return;
}
((FrameLayout) decorView).removeView(this.mCustomView);
if (this.mCustomView != null) {
((FrameLayout) decorView).removeView(this.mCustomView);
}
this.mCustomView = null;
decorView.setSystemUiVisibility(this.mOriginalSystemUiVisibility);
activity.setRequestedOrientation(this.mOriginalOrientation);
this.mCustomViewCallback.onCustomViewHidden();
if (this.mCustomViewCallback != null) {
this.mCustomViewCallback.onCustomViewHidden();
}
this.mCustomViewCallback = null;
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
@ -176,7 +182,9 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
this.mOriginalSystemUiVisibility = decorView.getSystemUiVisibility();
this.mOriginalOrientation = activity.getRequestedOrientation();
this.mCustomViewCallback = paramCustomViewCallback;
this.mCustomView.setBackgroundColor(Color.BLACK);
if (this.mCustomView != null) {
this.mCustomView.setBackgroundColor(Color.BLACK);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
decorView.setSystemUiVisibility(FULLSCREEN_SYSTEM_UI_VISIBILITY_KITKAT);
@ -185,7 +193,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
}
activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
((FrameLayout) decorView).addView(this.mCustomView, FULLSCREEN_LAYOUT_PARAMS);
if (inAppWebView != null) {
WebViewChannelDelegate eventWebViewChannelDelegate = inAppWebView.channelDelegate;
if (eventWebViewChannelDelegate != null)

View File

@ -625,6 +625,17 @@ public class InAppWebViewClient extends WebViewClient {
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequestExt request) {
final InAppWebView webView = (InAppWebView) view;
if (webView.webViewAssetLoaderExt != null && webView.webViewAssetLoaderExt.loader != null) {
try {
WebResourceResponse webResourceResponse = webView.webViewAssetLoaderExt.loader.shouldInterceptRequest(request.getUrl());
if (webResourceResponse != null) {
return webResourceResponse;
}
} catch (Exception e) {
e.printStackTrace();
}
}
if (webView.customSettings.useShouldInterceptRequest) {
WebResourceResponseExt response = null;
if (webView.channelDelegate != null) {

View File

@ -1,5 +1,8 @@
package com.pichillilorenzo.flutter_inappwebview.webview.in_app_webview;
import static android.webkit.WebSettings.LayoutAlgorithm.NARROW_COLUMNS;
import static android.webkit.WebSettings.LayoutAlgorithm.NORMAL;
import android.annotation.SuppressLint;
import android.os.Build;
import android.view.View;
@ -11,17 +14,14 @@ import androidx.webkit.WebSettingsCompat;
import androidx.webkit.WebViewFeature;
import com.pichillilorenzo.flutter_inappwebview.ISettings;
import com.pichillilorenzo.flutter_inappwebview.webview.InAppWebViewInterface;
import com.pichillilorenzo.flutter_inappwebview.types.PreferredContentModeOptionType;
import com.pichillilorenzo.flutter_inappwebview.webview.InAppWebViewInterface;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static android.webkit.WebSettings.LayoutAlgorithm.NARROW_COLUMNS;
import static android.webkit.WebSettings.LayoutAlgorithm.NORMAL;
public class InAppWebViewSettings implements ISettings<InAppWebViewInterface> {
public static final String LOG_TAG = "InAppWebViewSettings";
@ -52,7 +52,7 @@ public class InAppWebViewSettings implements ISettings<InAppWebViewInterface> {
public Boolean supportZoom = true;
public Boolean allowFileAccessFromFileURLs = false;
public Boolean allowUniversalAccessFromFileURLs = false;
public Boolean allowBackgroundAudioPlaying = false;
public Integer textZoom = 100;
public Boolean clearSessionCache = false;
public Boolean builtInZoomControls = true;
@ -119,6 +119,8 @@ public class InAppWebViewSettings implements ISettings<InAppWebViewInterface> {
@Nullable
public Integer requestedWithHeaderMode;
public Boolean enterpriseAuthenticationAppLinkPolicyEnabled = true;
@Nullable
public Map<String, Object> webViewAssetLoader;
@NonNull
@Override
@ -389,6 +391,12 @@ public class InAppWebViewSettings implements ISettings<InAppWebViewInterface> {
case "enterpriseAuthenticationAppLinkPolicyEnabled":
enterpriseAuthenticationAppLinkPolicyEnabled = (Boolean) value;
break;
case "allowBackgroundAudioPlaying":
allowBackgroundAudioPlaying = (Boolean) value;
break;
case "webViewAssetLoader":
webViewAssetLoader = (Map<String, Object>) value;
break;
}
}
@ -485,6 +493,7 @@ public class InAppWebViewSettings implements ISettings<InAppWebViewInterface> {
settings.put("algorithmicDarkeningAllowed", algorithmicDarkeningAllowed);
settings.put("requestedWithHeaderMode", requestedWithHeaderMode);
settings.put("enterpriseAuthenticationAppLinkPolicyEnabled", enterpriseAuthenticationAppLinkPolicyEnabled);
settings.put("allowBackgroundAudioPlaying", allowBackgroundAudioPlaying);
return settings;
}

View File

@ -492,9 +492,9 @@ class ExchangeableObjectGenerator
final isNullable = Util.typeIsNullable(elementType);
if (elementType.getDisplayString(withNullability: false) == "Uri") {
if (!isNullable) {
return "Uri.parse($value)";
return "(Uri.tryParse($value) ?? Uri())";
} else {
return "$value != null ? Uri.parse($value) : null";
return "$value != null ? Uri.tryParse($value) : null";
}
} else if (elementType.getDisplayString(withNullability: false) ==
"Color") {

View File

@ -12,7 +12,7 @@
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<style name="NormalTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>WebViewAssetLoader</title>
</head>
<body>
<h1>WebViewAssetLoader</h1>
<p>This is a test.</p>
</body>
</html>

View File

@ -23,3 +23,6 @@ final TEST_CHROME_SAFE_BROWSING_MALWARE =
final TEST_PERMISSION_SITE = Uri.parse('https://permission.site/');
final TEST_SERVICE_WORKER_URL = Uri.parse(
'https://mdn.github.io/dom-examples/service-worker/simple-service-worker/');
final TEST_WEBVIEW_ASSET_LOADER_DOMAIN = 'my.custom.domain.com';
final TEST_WEBVIEW_ASSET_LOADER_URL = Uri.parse(
'https://$TEST_WEBVIEW_ASSET_LOADER_DOMAIN/assets/flutter_assets/assets/website/index.html');

View File

@ -12,6 +12,6 @@ void main() {
takeScreenshot();
customSize();
setGetSettings();
convertToInAppWebView();
// convertToInAppWebView();
});
}

View File

@ -82,6 +82,7 @@ import 'user_scripts.dart';
import 'video_playback_policy.dart';
import 'web_history.dart';
import 'web_message.dart';
import 'webview_asset_loader.dart';
import 'webview_windows.dart';
void main() {
@ -170,5 +171,6 @@ void main() {
createPdf();
applePayAPI();
handlesURLScheme();
webViewAssetLoader();
}, skip: shouldSkip);
}

View File

@ -0,0 +1,56 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_test/flutter_test.dart';
import '../constants.dart';
void webViewAssetLoader() {
final shouldSkip = kIsWeb
? true
: ![
TargetPlatform.android,
].contains(defaultTargetPlatform);
testWidgets('WebViewAssetLoader', (WidgetTester tester) async {
final Completer<InAppWebViewController> controllerCompleter =
Completer<InAppWebViewController>();
final Completer<String> pageLoaded = Completer<String>();
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: InAppWebView(
key: GlobalKey(),
initialUrlRequest: URLRequest(url: TEST_WEBVIEW_ASSET_LOADER_URL),
initialSettings: InAppWebViewSettings(
allowFileAccessFromFileURLs: false,
allowUniversalAccessFromFileURLs: false,
allowFileAccess: false,
allowContentAccess: false,
webViewAssetLoader: WebViewAssetLoader(
domain: TEST_WEBVIEW_ASSET_LOADER_DOMAIN,
pathHandlers: [AssetsPathHandler(path: '/assets/')])),
onWebViewCreated: (controller) {
controllerCompleter.complete(controller);
},
onLoadStop: (controller, url) {
pageLoaded.complete(url.toString());
},
),
),
);
final InAppWebViewController controller = await controllerCompleter.future;
final url = await pageLoaded.future;
expect(url, TEST_WEBVIEW_ASSET_LOADER_URL.toString());
expect(
await controller.evaluateJavascript(
source: "document.querySelector('h1').innerHTML"),
'WebViewAssetLoader');
}, skip: shouldSkip);
}

View File

@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>WebViewAssetLoader</title>
</head>
<body>
<h1>WebViewAssetLoader</h1>
<p>This is a test.</p>
</body>
</html>

View File

@ -1,3 +1,10 @@
export 'service_worker_controller.dart';
export 'webview_feature.dart' show WebViewFeature, AndroidWebViewFeature;
export 'proxy_controller.dart';
export 'webview_asset_loader.dart'
show
WebViewAssetLoader,
PathHandler,
AssetsPathHandler,
ResourcesPathHandler,
InternalStoragePathHandler;

View File

@ -0,0 +1,181 @@
import 'package:flutter/services.dart';
import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart';
import '../types/web_resource_response.dart';
import '../util.dart';
import '../in_app_webview/webview.dart';
part 'webview_asset_loader.g.dart';
///Helper class to load local files including application's static assets and resources using http(s):// URLs inside a [WebView] class.
///Loading local files using web-like URLs instead of `file://` is desirable as it is compatible with the Same-Origin policy.
///
///For more context about application's assets and resources and how to normally access them please refer to
///[Android Developer Docs: App resources overview](https://developer.android.com/guide/topics/resources/providing-resources).
///
///Using http(s):// URLs to access local resources may conflict with a real website.
///This means that local files should only be hosted on domains your organization owns
///(at paths reserved for this purpose) or the default domain reserved for this: `appassets.androidplatform.net`.
///
///**Supported Platforms/Implementations**:
///- Android native WebView
@ExchangeableObject(copyMethod: true)
class WebViewAssetLoader_ {
///An unused domain reserved for Android applications to intercept requests for app assets.
///
///It is used by default unless the user specified a different domain.
static final String DEFAULT_DOMAIN = "appassets.androidplatform.net";
///Set the domain under which app assets can be accessed. The default domain is `appassets.androidplatform.net`.
String? domain;
///Allow using the HTTP scheme in addition to HTTPS. The default is to not allow HTTP.
bool? httpAllowed;
///List of registered path handlers.
///
///[WebViewAssetLoader] will try Path Handlers in the order they're registered,
///and will use whichever is the first to return a non-null.
List<PathHandler_>? pathHandlers;
WebViewAssetLoader_({this.domain, this.httpAllowed, this.pathHandlers});
}
///A handler that produces responses for a registered path.
///
///Implement this interface to handle other use-cases according to your app's needs.
@ExchangeableObject(fromMapFactory: false)
abstract class PathHandler_ {
late final String _type;
late final String _id;
late final MethodChannel _channel;
///The suffix path to be handled.
///
///The path should start and end with a `"/"` and it shouldn't collide with a real web path.
String path;
@ExchangeableObjectConstructor()
PathHandler_({required this.path}) {
_type = this.runtimeType.toString();
_id = IdGenerator.generate();
this._channel = MethodChannel(
'com.pichillilorenzo/flutter_inappwebview_custompathhandler_$_id');
this._channel.setMethodCallHandler((call) async {
try {
return await _handleMethod(call);
} on Error catch (e) {
print(e);
print(e.stackTrace);
}
});
}
Future<dynamic> _handleMethod(MethodCall call) async {
switch (call.method) {
case "handle":
String path = call.arguments["path"];
return (await handle(path))?.toMap();
default:
throw UnimplementedError("Unimplemented ${call.method} method");
}
}
///Handles the requested URL by returning the appropriate response.
///
///Returning a `null` value means that the handler decided not to handle this path.
///In this case, [WebViewAssetLoader] will try the next handler registered on this path or pass to [WebView] that will fall back to network to try to resolve the URL.
///
///However, if the handler wants to save unnecessary processing either by another handler or by falling back to network,
///in cases like a file cannot be found, it may return a `WebResourceResponse(data: null)`
///which is received as an HTTP response with status code `404` and no body.
Future<WebResourceResponse?> handle(String path) async {
return null;
}
@ExchangeableObjectMethod(toMapMergeWith: true)
// ignore: unused_element
Map<String, dynamic> _toMapMergeWith() {
return {"type": _type, "id": _id};
}
}
///Handler class to open a file from assets directory in the application APK.
///
///Opens the requested file from the application's assets directory.
///
///The matched prefix path used shouldn't be a prefix of a real web path.
///Thus, if the requested file cannot be found a [WebResourceResponse] object with a `null` data will be returned instead of `null`.
///This saves the time of falling back to network and trying to resolve a path that doesn't exist.
///A [WebResourceResponse] with `null` data will be received as an HTTP response with status code `404` and no body.
///
///The MIME type for the file will be determined from the file's extension using
///[guessContentTypeFromName](https://developer.android.com/reference/java/net/URLConnection.html#guessContentTypeFromName-java.lang.String-).
///Developers should ensure that asset files are named using standard file extensions.
///If the file does not have a recognised extension, `text/plain` will be used by default.
@ExchangeableObject()
class AssetsPathHandler_ extends PathHandler_ {
AssetsPathHandler_({required String path}) : super(path: path) {}
@ExchangeableObjectMethod(toMapMergeWith: true)
// ignore: unused_element
Map<String, dynamic> _toMapMergeWith() {
return {"type": _type};
}
}
///Handler class to open a file from resources directory in the application APK.
///
///Opens the requested file from application's resources directory.
///
///The matched prefix path used shouldn't be a prefix of a real web path.
///Thus, if the requested file cannot be found a [WebResourceResponse] object with a `null` data will be returned instead of `null`.
///This saves the time of falling back to network and trying to resolve a path that doesn't exist.
///A [WebResourceResponse] with `null` data will be received as an HTTP response with status code `404` and no body.
///
///The MIME type for the file will be determined from the file's extension using
///[guessContentTypeFromName](https://developer.android.com/reference/java/net/URLConnection.html#guessContentTypeFromName-java.lang.String-).
///Developers should ensure that asset files are named using standard file extensions.
///If the file does not have a recognised extension, `text/plain` will be used by default.
@ExchangeableObject()
class ResourcesPathHandler_ extends PathHandler_ {
ResourcesPathHandler_({required String path}) : super(path: path) {}
@ExchangeableObjectMethod(toMapMergeWith: true)
// ignore: unused_element
Map<String, dynamic> _toMapMergeWith() {
return {"type": _type};
}
}
///Handler class to open files from application internal storage.
///For more information about android storage please refer to
///[Android Developers Docs: Data and file storage overview](https://developer.android.com/guide/topics/data/data-storage).
///
///To avoid leaking user or app data to the web, make sure to choose [directory] carefully,
///and assume any file under this directory could be accessed by any web page subject to same-origin rules.
///
///Opens the requested file from the exposed data directory.
///
///The matched prefix path used shouldn't be a prefix of a real web path.
///Thus, if the requested file cannot be found a [WebResourceResponse] object with a `null` data will be returned instead of `null`.
///This saves the time of falling back to network and trying to resolve a path that doesn't exist.
///A [WebResourceResponse] with `null` data will be received as an HTTP response with status code `404` and no body.
///
///The MIME type for the file will be determined from the file's extension using
///[guessContentTypeFromName](https://developer.android.com/reference/java/net/URLConnection.html#guessContentTypeFromName-java.lang.String-).
///Developers should ensure that asset files are named using standard file extensions.
///If the file does not have a recognised extension, `text/plain` will be used by default.
@ExchangeableObject()
class InternalStoragePathHandler_ extends PathHandler_ {
String directory;
InternalStoragePathHandler_({required String path, required this.directory})
: super(path: path) {}
@ExchangeableObjectMethod(toMapMergeWith: true)
// ignore: unused_element
Map<String, dynamic> _toMapMergeWith() {
return {"type": _type};
}
}

View File

@ -0,0 +1,312 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'webview_asset_loader.dart';
// **************************************************************************
// ExchangeableObjectGenerator
// **************************************************************************
///Helper class to load local files including application's static assets and resources using http(s):// URLs inside a [WebView] class.
///Loading local files using web-like URLs instead of `file://` is desirable as it is compatible with the Same-Origin policy.
///
///For more context about application's assets and resources and how to normally access them please refer to
///[Android Developer Docs: App resources overview](https://developer.android.com/guide/topics/resources/providing-resources).
///
///Using http(s):// URLs to access local resources may conflict with a real website.
///This means that local files should only be hosted on domains your organization owns
///(at paths reserved for this purpose) or the default domain reserved for this: `appassets.androidplatform.net`.
///
///**Supported Platforms/Implementations**:
///- Android native WebView
class WebViewAssetLoader {
///An unused domain reserved for Android applications to intercept requests for app assets.
///
///It is used by default unless the user specified a different domain.
static final String DEFAULT_DOMAIN = "appassets.androidplatform.net";
///Set the domain under which app assets can be accessed. The default domain is `appassets.androidplatform.net`.
String? domain;
///Allow using the HTTP scheme in addition to HTTPS. The default is to not allow HTTP.
bool? httpAllowed;
///List of registered path handlers.
///
///[WebViewAssetLoader] will try Path Handlers in the order they're registered,
///and will use whichever is the first to return a non-null.
List<PathHandler>? pathHandlers;
WebViewAssetLoader({this.domain, this.httpAllowed, this.pathHandlers});
///Gets a possible [WebViewAssetLoader] instance from a [Map] value.
static WebViewAssetLoader? fromMap(Map<String, dynamic>? map) {
if (map == null) {
return null;
}
final instance = WebViewAssetLoader(
domain: map['domain'],
httpAllowed: map['httpAllowed'],
pathHandlers: map['pathHandlers'] != null
? List<PathHandler>.from(map['pathHandlers'].map((e) => e))
: null,
);
return instance;
}
///Converts instance to a map.
Map<String, dynamic> toMap() {
return {
"domain": domain,
"httpAllowed": httpAllowed,
"pathHandlers": pathHandlers?.map((e) => e.toMap()).toList(),
};
}
///Converts instance to a map.
Map<String, dynamic> toJson() {
return toMap();
}
///Returns a copy of WebViewAssetLoader.
WebViewAssetLoader copy() {
return WebViewAssetLoader.fromMap(toMap()) ?? WebViewAssetLoader();
}
@override
String toString() {
return 'WebViewAssetLoader{domain: $domain, httpAllowed: $httpAllowed, pathHandlers: $pathHandlers}';
}
}
///A handler that produces responses for a registered path.
///
///Implement this interface to handle other use-cases according to your app's needs.
abstract class PathHandler {
late final String _type;
late final String _id;
late final MethodChannel _channel;
///The suffix path to be handled.
///
///The path should start and end with a `"/"` and it shouldn't collide with a real web path.
String path;
PathHandler({required this.path}) {
_type = this.runtimeType.toString();
_id = IdGenerator.generate();
this._channel = MethodChannel(
'com.pichillilorenzo/flutter_inappwebview_custompathhandler_$_id');
this._channel.setMethodCallHandler((call) async {
try {
return await _handleMethod(call);
} on Error catch (e) {
print(e);
print(e.stackTrace);
}
});
}
Future<dynamic> _handleMethod(MethodCall call) async {
switch (call.method) {
case "handle":
String path = call.arguments["path"];
return (await handle(path))?.toMap();
default:
throw UnimplementedError("Unimplemented ${call.method} method");
}
}
///Handles the requested URL by returning the appropriate response.
///
///Returning a `null` value means that the handler decided not to handle this path.
///In this case, [WebViewAssetLoader] will try the next handler registered on this path or pass to [WebView] that will fall back to network to try to resolve the URL.
///
///However, if the handler wants to save unnecessary processing either by another handler or by falling back to network,
///in cases like a file cannot be found, it may return a `WebResourceResponse(data: null)`
///which is received as an HTTP response with status code `404` and no body.
Future<WebResourceResponse?> handle(String path) async {
return null;
}
@ExchangeableObjectMethod(toMapMergeWith: true)
Map<String, dynamic> _toMapMergeWith() {
return {"type": _type, "id": _id};
}
///Converts instance to a map.
Map<String, dynamic> toMap() {
return {
..._toMapMergeWith(),
"path": path,
};
}
///Converts instance to a map.
Map<String, dynamic> toJson() {
return toMap();
}
@override
String toString() {
return 'PathHandler{path: $path}';
}
}
///Handler class to open a file from assets directory in the application APK.
///
///Opens the requested file from the application's assets directory.
///
///The matched prefix path used shouldn't be a prefix of a real web path.
///Thus, if the requested file cannot be found a [WebResourceResponse] object with a `null` data will be returned instead of `null`.
///This saves the time of falling back to network and trying to resolve a path that doesn't exist.
///A [WebResourceResponse] with `null` data will be received as an HTTP response with status code `404` and no body.
///
///The MIME type for the file will be determined from the file's extension using
///[guessContentTypeFromName](https://developer.android.com/reference/java/net/URLConnection.html#guessContentTypeFromName-java.lang.String-).
///Developers should ensure that asset files are named using standard file extensions.
///If the file does not have a recognised extension, `text/plain` will be used by default.
class AssetsPathHandler extends PathHandler {
AssetsPathHandler({required String path}) : super(path: path);
///Gets a possible [AssetsPathHandler] instance from a [Map] value.
static AssetsPathHandler? fromMap(Map<String, dynamic>? map) {
if (map == null) {
return null;
}
final instance = AssetsPathHandler(
path: map['path'],
);
return instance;
}
@ExchangeableObjectMethod(toMapMergeWith: true)
Map<String, dynamic> _toMapMergeWith() {
return {"type": _type};
}
///Converts instance to a map.
Map<String, dynamic> toMap() {
return {
..._toMapMergeWith(),
"path": path,
};
}
///Converts instance to a map.
Map<String, dynamic> toJson() {
return toMap();
}
@override
String toString() {
return 'AssetsPathHandler{path: $path}';
}
}
///Handler class to open a file from resources directory in the application APK.
///
///Opens the requested file from application's resources directory.
///
///The matched prefix path used shouldn't be a prefix of a real web path.
///Thus, if the requested file cannot be found a [WebResourceResponse] object with a `null` data will be returned instead of `null`.
///This saves the time of falling back to network and trying to resolve a path that doesn't exist.
///A [WebResourceResponse] with `null` data will be received as an HTTP response with status code `404` and no body.
///
///The MIME type for the file will be determined from the file's extension using
///[guessContentTypeFromName](https://developer.android.com/reference/java/net/URLConnection.html#guessContentTypeFromName-java.lang.String-).
///Developers should ensure that asset files are named using standard file extensions.
///If the file does not have a recognised extension, `text/plain` will be used by default.
class ResourcesPathHandler extends PathHandler {
ResourcesPathHandler({required String path}) : super(path: path);
///Gets a possible [ResourcesPathHandler] instance from a [Map] value.
static ResourcesPathHandler? fromMap(Map<String, dynamic>? map) {
if (map == null) {
return null;
}
final instance = ResourcesPathHandler(
path: map['path'],
);
return instance;
}
@ExchangeableObjectMethod(toMapMergeWith: true)
Map<String, dynamic> _toMapMergeWith() {
return {"type": _type};
}
///Converts instance to a map.
Map<String, dynamic> toMap() {
return {
..._toMapMergeWith(),
"path": path,
};
}
///Converts instance to a map.
Map<String, dynamic> toJson() {
return toMap();
}
@override
String toString() {
return 'ResourcesPathHandler{path: $path}';
}
}
///Handler class to open files from application internal storage.
///For more information about android storage please refer to
///[Android Developers Docs: Data and file storage overview](https://developer.android.com/guide/topics/data/data-storage).
///
///To avoid leaking user or app data to the web, make sure to choose [directory] carefully,
///and assume any file under this directory could be accessed by any web page subject to same-origin rules.
///
///Opens the requested file from the exposed data directory.
///
///The matched prefix path used shouldn't be a prefix of a real web path.
///Thus, if the requested file cannot be found a [WebResourceResponse] object with a `null` data will be returned instead of `null`.
///This saves the time of falling back to network and trying to resolve a path that doesn't exist.
///A [WebResourceResponse] with `null` data will be received as an HTTP response with status code `404` and no body.
///
///The MIME type for the file will be determined from the file's extension using
///[guessContentTypeFromName](https://developer.android.com/reference/java/net/URLConnection.html#guessContentTypeFromName-java.lang.String-).
///Developers should ensure that asset files are named using standard file extensions.
///If the file does not have a recognised extension, `text/plain` will be used by default.
class InternalStoragePathHandler extends PathHandler {
String directory;
InternalStoragePathHandler({required this.directory, required String path})
: super(path: path);
///Gets a possible [InternalStoragePathHandler] instance from a [Map] value.
static InternalStoragePathHandler? fromMap(Map<String, dynamic>? map) {
if (map == null) {
return null;
}
final instance = InternalStoragePathHandler(
path: map['path'],
directory: map['directory'],
);
return instance;
}
@ExchangeableObjectMethod(toMapMergeWith: true)
Map<String, dynamic> _toMapMergeWith() {
return {"type": _type};
}
///Converts instance to a map.
Map<String, dynamic> toMap() {
return {
..._toMapMergeWith(),
"path": path,
"directory": directory,
};
}
///Converts instance to a map.
Map<String, dynamic> toJson() {
return toMap();
}
@override
String toString() {
return 'InternalStoragePathHandler{path: $path, directory: $directory}';
}
}

View File

@ -11,6 +11,9 @@ import '../types/user_script_injection_time.dart';
part 'webview_feature.g.dart';
///Class that represents an Android-specific utility class for checking which WebView Support Library features are supported on the device.
///
///**Supported Platforms/Implementations**:
///- Android native WebView
@ExchangeableEnum()
class WebViewFeature_ {
@ExchangeableEnumCustomValue()

View File

@ -7,6 +7,9 @@ part of 'webview_feature.dart';
// **************************************************************************
///Class that represents an Android-specific utility class for checking which WebView Support Library features are supported on the device.
///
///**Supported Platforms/Implementations**:
///- Android native WebView
class WebViewFeature {
final String _value;
final String _nativeValue;

View File

@ -121,6 +121,6 @@ class AndroidInAppWebViewController {
Future<Uri?> getOriginalUrl() async {
Map<String, dynamic> args = <String, dynamic>{};
String? url = await _channel.invokeMethod('getOriginalUrl', args);
return url != null ? Uri.parse(url) : null;
return url != null ? Uri.tryParse(url) : null;
}
}

View File

@ -368,7 +368,7 @@ class IOSInAppWebViewOptions
instance.useOnNavigationResponse = map["useOnNavigationResponse"];
instance.applePayAPIEnabled = map["applePayAPIEnabled"];
instance.allowingReadAccessTo = map["allowingReadAccessTo"] != null
? Uri.parse(map["allowingReadAccessTo"])
? Uri.tryParse(map["allowingReadAccessTo"])
: null;
instance.disableLongPressContextMenuOnLinks =
map["disableLongPressContextMenuOnLinks"];

View File

@ -646,77 +646,52 @@ class _InAppWebViewState extends State<InAppWebView> {
if (!useHybridComposition && widget.pullToRefreshController != null) {
throw new Exception(
"To use the pull-to-refresh feature, useHybridComposition Android-specific option MUST be true!");
"To use the pull-to-refresh feature, InAppWebViewSettings.useHybridComposition setting MUST be true!");
}
if (useHybridComposition) {
return PlatformViewLink(
viewType: 'com.pichillilorenzo/flutter_inappwebview',
surfaceFactory: (
BuildContext context,
PlatformViewController controller,
) {
return AndroidViewSurface(
controller: controller as AndroidViewController,
gestureRecognizers: widget.gestureRecognizers ??
const <Factory<OneSequenceGestureRecognizer>>{},
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
);
},
onCreatePlatformView: (PlatformViewCreationParams params) {
return PlatformViewsService.initSurfaceAndroidView(
id: params.id,
viewType: 'com.pichillilorenzo/flutter_inappwebview',
layoutDirection: TextDirection.rtl,
creationParams: <String, dynamic>{
'initialUrlRequest': widget.initialUrlRequest?.toMap(),
'initialFile': widget.initialFile,
'initialData': widget.initialData?.toMap(),
'initialSettings': initialSettings,
'contextMenu': widget.contextMenu?.toMap() ?? {},
'windowId': widget.windowId,
'headlessWebViewId':
widget.headlessWebView?.isRunning() ?? false
? widget.headlessWebView?.id
: null,
'implementation': widget.implementation.toNativeValue(),
'initialUserScripts':
widget.initialUserScripts?.map((e) => e.toMap()).toList() ??
[],
'pullToRefreshSettings': pullToRefreshSettings
},
creationParamsCodec: const StandardMessageCodec(),
)
..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
..addOnPlatformViewCreatedListener(
(id) => _onPlatformViewCreated(id))
..create();
},
);
} else {
return AndroidView(
viewType: 'com.pichillilorenzo/flutter_inappwebview',
onPlatformViewCreated: _onPlatformViewCreated,
gestureRecognizers: widget.gestureRecognizers,
layoutDirection: Directionality.maybeOf(context) ?? TextDirection.rtl,
creationParams: <String, dynamic>{
'initialUrlRequest': widget.initialUrlRequest?.toMap(),
'initialFile': widget.initialFile,
'initialData': widget.initialData?.toMap(),
'initialSettings': initialSettings,
'contextMenu': widget.contextMenu?.toMap() ?? {},
'windowId': widget.windowId,
'headlessWebViewId': widget.headlessWebView?.isRunning() ?? false
? widget.headlessWebView?.id
: null,
'implementation': widget.implementation.toNativeValue(),
'initialUserScripts':
widget.initialUserScripts?.map((e) => e.toMap()).toList() ?? [],
'pullToRefreshSettings': pullToRefreshSettings
},
creationParamsCodec: const StandardMessageCodec(),
);
}
return PlatformViewLink(
viewType: 'com.pichillilorenzo/flutter_inappwebview',
surfaceFactory: (
BuildContext context,
PlatformViewController controller,
) {
return AndroidViewSurface(
controller: controller as AndroidViewController,
gestureRecognizers: widget.gestureRecognizers ??
const <Factory<OneSequenceGestureRecognizer>>{},
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
);
},
onCreatePlatformView: (PlatformViewCreationParams params) {
return _createAndroidViewController(
hybridComposition: useHybridComposition,
id: params.id,
viewType: 'com.pichillilorenzo/flutter_inappwebview',
layoutDirection:
Directionality.maybeOf(context) ?? TextDirection.rtl,
creationParams: <String, dynamic>{
'initialUrlRequest': widget.initialUrlRequest?.toMap(),
'initialFile': widget.initialFile,
'initialData': widget.initialData?.toMap(),
'initialSettings': initialSettings,
'contextMenu': widget.contextMenu?.toMap() ?? {},
'windowId': widget.windowId,
'headlessWebViewId': widget.headlessWebView?.isRunning() ?? false
? widget.headlessWebView?.id
: null,
'implementation': widget.implementation.toNativeValue(),
'initialUserScripts':
widget.initialUserScripts?.map((e) => e.toMap()).toList() ??
[],
'pullToRefreshSettings': pullToRefreshSettings
},
)
..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
..addOnPlatformViewCreatedListener(
(id) => _onPlatformViewCreated(id))
..create();
},
);
} else if (defaultTargetPlatform ==
TargetPlatform
.iOS /* || defaultTargetPlatform == TargetPlatform.macOS*/) {
@ -763,6 +738,31 @@ class _InAppWebViewState extends State<InAppWebView> {
_controller = null;
}
AndroidViewController _createAndroidViewController({
required bool hybridComposition,
required int id,
required String viewType,
required TextDirection layoutDirection,
required Map<String, dynamic> creationParams,
}) {
if (hybridComposition) {
return PlatformViewsService.initExpensiveAndroidView(
id: id,
viewType: viewType,
layoutDirection: layoutDirection,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
);
}
return PlatformViewsService.initSurfaceAndroidView(
id: id,
viewType: viewType,
layoutDirection: layoutDirection,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
);
}
void _onPlatformViewCreated(int id) {
final viewId = (!kIsWeb && (widget.headlessWebView?.isRunning() ?? false))
? widget.headlessWebView?.id

View File

@ -134,7 +134,7 @@ class InAppWebViewController {
if ((_webview != null && _webview!.onLoadStart != null) ||
_inAppBrowser != null) {
String? url = call.arguments["url"];
Uri? uri = url != null ? Uri.parse(url) : null;
Uri? uri = url != null ? Uri.tryParse(url) : null;
if (_webview != null && _webview!.onLoadStart != null)
_webview!.onLoadStart!(this, uri);
else
@ -145,7 +145,7 @@ class InAppWebViewController {
if ((_webview != null && _webview!.onLoadStop != null) ||
_inAppBrowser != null) {
String? url = call.arguments["url"];
Uri? uri = url != null ? Uri.parse(url) : null;
Uri? uri = url != null ? Uri.tryParse(url) : null;
if (_webview != null && _webview!.onLoadStop != null)
_webview!.onLoadStop!(this, uri);
else
@ -435,7 +435,7 @@ class InAppWebViewController {
_webview!.androidOnRenderProcessUnresponsive != null)) ||
_inAppBrowser != null) {
String? url = call.arguments["url"];
Uri? uri = url != null ? Uri.parse(url) : null;
Uri? uri = url != null ? Uri.tryParse(url) : null;
if (_webview != null) {
if (_webview!.onRenderProcessUnresponsive != null)
@ -463,7 +463,7 @@ class InAppWebViewController {
_webview!.androidOnRenderProcessResponsive != null)) ||
_inAppBrowser != null) {
String? url = call.arguments["url"];
Uri? uri = url != null ? Uri.parse(url) : null;
Uri? uri = url != null ? Uri.tryParse(url) : null;
if (_webview != null) {
if (_webview!.onRenderProcessResponsive != null)
@ -516,7 +516,7 @@ class InAppWebViewController {
_webview!.androidOnFormResubmission != null)) ||
_inAppBrowser != null) {
String? url = call.arguments["url"];
Uri? uri = url != null ? Uri.parse(url) : null;
Uri? uri = url != null ? Uri.tryParse(url) : null;
if (_webview != null) {
if (_webview!.onFormResubmission != null)
@ -589,7 +589,7 @@ class InAppWebViewController {
_inAppBrowser != null) {
String url = call.arguments["url"];
bool precomposed = call.arguments["precomposed"];
Uri uri = Uri.parse(url);
Uri uri = Uri.tryParse(url) ?? Uri();
if (_webview != null) {
if (_webview!.onReceivedTouchIconUrl != null)
@ -689,7 +689,7 @@ class InAppWebViewController {
String url = call.arguments["url"];
SafeBrowsingThreat? threatType =
SafeBrowsingThreat.fromNativeValue(call.arguments["threatType"]);
Uri uri = Uri.parse(url);
Uri uri = Uri.tryParse(url) ?? Uri();
if (_webview != null) {
if (_webview!.onSafeBrowsingHit != null)
@ -867,7 +867,7 @@ class InAppWebViewController {
_inAppBrowser != null) {
String? url = call.arguments["url"];
bool? isReload = call.arguments["isReload"];
Uri? uri = url != null ? Uri.parse(url) : null;
Uri? uri = url != null ? Uri.tryParse(url) : null;
if (_webview != null && _webview!.onUpdateVisitedHistory != null)
_webview!.onUpdateVisitedHistory!(this, uri, isReload);
else
@ -895,7 +895,7 @@ class InAppWebViewController {
if ((_webview != null && _webview!.onPageCommitVisible != null) ||
_inAppBrowser != null) {
String? url = call.arguments["url"];
Uri? uri = url != null ? Uri.parse(url) : null;
Uri? uri = url != null ? Uri.tryParse(url) : null;
if (_webview != null && _webview!.onPageCommitVisible != null)
_webview!.onPageCommitVisible!(this, uri);
else
@ -1120,7 +1120,7 @@ class InAppWebViewController {
_inAppBrowser != null) {
String? url = call.arguments["url"];
String? printJobId = call.arguments["printJobId"];
Uri? uri = url != null ? Uri.parse(url) : null;
Uri? uri = url != null ? Uri.tryParse(url) : null;
PrintJobController? printJob =
printJobId != null ? PrintJobController(id: printJobId) : null;
@ -1333,7 +1333,7 @@ class InAppWebViewController {
Future<Uri?> getUrl() async {
Map<String, dynamic> args = <String, dynamic>{};
String? url = await _channel.invokeMethod('getUrl', args);
return url != null ? Uri.parse(url) : null;
return url != null ? Uri.tryParse(url) : null;
}
///Gets the title for the current page.
@ -1574,11 +1574,17 @@ class InAppWebViewController {
int width = int.parse(size.split("x")[0]);
int height = int.parse(size.split("x")[1]);
favicons.add(Favicon(
url: Uri.parse(urlIcon), rel: rel, width: width, height: height));
url: Uri.tryParse(urlIcon) ?? Uri(),
rel: rel,
width: width,
height: height));
}
} else {
favicons.add(Favicon(
url: Uri.parse(urlIcon), rel: rel, width: null, height: null));
url: Uri.tryParse(urlIcon) ?? Uri(),
rel: rel,
width: null,
height: null));
}
return favicons;
@ -2450,7 +2456,7 @@ class InAppWebViewController {
Future<Uri?> getOriginalUrl() async {
Map<String, dynamic> args = <String, dynamic>{};
String? url = await _channel.invokeMethod('getOriginalUrl', args);
return url != null ? Uri.parse(url) : null;
return url != null ? Uri.tryParse(url) : null;
}
///Gets the current zoom scale of the WebView.
@ -2547,7 +2553,7 @@ class InAppWebViewController {
await _channel.invokeMethod('requestFocusNodeHref', args);
return result != null
? RequestFocusNodeHrefResult(
url: result['url'] != null ? Uri.parse(result['url']) : null,
url: result['url'] != null ? Uri.tryParse(result['url']) : null,
title: result['title'],
src: result['src'],
)
@ -2567,7 +2573,7 @@ class InAppWebViewController {
await _channel.invokeMethod('requestImageRef', args);
return result != null
? RequestImageRefResult(
url: result['url'] != null ? Uri.parse(result['url']) : null,
url: result['url'] != null ? Uri.tryParse(result['url']) : null,
)
: null;
}
@ -3014,7 +3020,7 @@ class InAppWebViewController {
Future<void> postWebMessage(
{required WebMessage message, Uri? targetOrigin}) async {
if (targetOrigin == null) {
targetOrigin = Uri.parse("");
targetOrigin = Uri();
}
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('message', () => message.toMap());
@ -3604,7 +3610,7 @@ class InAppWebViewController {
Map<String, dynamic> args = <String, dynamic>{};
String? url = await _staticChannel.invokeMethod(
'getSafeBrowsingPrivacyPolicyUrl', args);
return url != null ? Uri.parse(url) : null;
return url != null ? Uri.tryParse(url) : null;
}
///Use [setSafeBrowsingAllowlist] instead.

View File

@ -3,6 +3,7 @@ import 'package:flutter/widgets.dart';
import 'package:flutter_inappwebview/src/types/user_preferred_content_mode.dart';
import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart';
import '../android/webview_asset_loader.dart';
import '../types/action_mode_menu_item.dart';
import '../types/cache_mode.dart';
import '../types/data_detector_types.dart';
@ -319,6 +320,25 @@ class InAppWebViewSettings_ {
///- MacOS
bool? allowUniversalAccessFromFileURLs;
///Set to `true` to allow audio playing when the app goes in background or the screen is locked or another app is opened.
///However, there will be no controls in the notification bar or on the lockscreen.
///Also, make sure to not call [InAppWebViewController.pause], otherwise it will stop audio playing.
///The default value is `false`.
///
///**IMPORTANT NOTE**: if you use this setting, your app could be rejected by the Google Play Store.
///For example, if you allow background playing of YouTube videos, which is a violation of the YouTube API Terms of Service.
///
///**Supported Platforms/Implementations**:
///- Android native WebView
bool? allowBackgroundAudioPlaying;
///Use a [WebViewAssetLoader] instance to load local files including application's static assets and resources using http(s):// URLs.
///Loading local files using web-like URLs instead of `file://` is desirable as it is compatible with the Same-Origin policy.
///
///**Supported Platforms/Implementations**:
///- Android native WebView
WebViewAssetLoader_? webViewAssetLoader;
///Sets the text zoom of the page in percent. The default value is `100`.
///
///**Supported Platforms/Implementations**:
@ -1251,6 +1271,7 @@ class InAppWebViewSettings_ {
///- Web
String? iframeCsp;
@ExchangeableObjectConstructor()
InAppWebViewSettings_({
this.useShouldOverrideUrlLoading = false,
this.useOnLoadResource = false,
@ -1381,6 +1402,8 @@ class InAppWebViewSettings_ {
this.isFindInteractionEnabled = false,
this.minimumViewportInset,
this.maximumViewportInset,
this.allowBackgroundAudioPlaying = false,
this.webViewAssetLoader,
this.iframeAllow,
this.iframeAllowFullscreen,
this.iframeSandbox,
@ -1409,372 +1432,6 @@ class InAppWebViewSettings_ {
maximumViewportInset!.horizontal,
"minimumViewportInset cannot be larger than maximumViewportInset");
}
// Map<String, dynamic> toMap() {
// List<Map<String, Map<String, dynamic>>> contentBlockersMapList = [];
// contentBlockers.forEach((contentBlocker) {
// contentBlockersMapList.add(contentBlocker.toMap());
// });
// List<String> dataDetectorTypesList = [];
// dataDetectorTypes.forEach((dataDetectorType) {
// dataDetectorTypesList.add(dataDetectorType.toNativeValue());
// });
//
// return {
// "useShouldOverrideUrlLoading": useShouldOverrideUrlLoading,
// "useOnLoadResource": useOnLoadResource,
// "useOnDownloadStart": useOnDownloadStart,
// "clearCache": clearCache,
// "userAgent": userAgent,
// "applicationNameForUserAgent": applicationNameForUserAgent,
// "javaScriptEnabled": javaScriptEnabled,
// "javaScriptCanOpenWindowsAutomatically":
// javaScriptCanOpenWindowsAutomatically,
// "mediaPlaybackRequiresUserGesture": mediaPlaybackRequiresUserGesture,
// "verticalScrollBarEnabled": verticalScrollBarEnabled,
// "horizontalScrollBarEnabled": horizontalScrollBarEnabled,
// "resourceCustomSchemes": resourceCustomSchemes,
// "contentBlockers": contentBlockersMapList,
// "preferredContentMode": preferredContentMode?.toNativeValue(),
// "useShouldInterceptAjaxRequest": useShouldInterceptAjaxRequest,
// "useShouldInterceptFetchRequest": useShouldInterceptFetchRequest,
// "incognito": incognito,
// "cacheEnabled": cacheEnabled,
// "transparentBackground": transparentBackground,
// "disableVerticalScroll": disableVerticalScroll,
// "disableHorizontalScroll": disableHorizontalScroll,
// "disableContextMenu": disableContextMenu,
// "supportZoom": supportZoom,
// "allowFileAccessFromFileURLs": allowFileAccessFromFileURLs,
// "allowUniversalAccessFromFileURLs": allowUniversalAccessFromFileURLs,
// "textZoom": textZoom,
// "clearSessionCache": clearSessionCache,
// "builtInZoomControls": builtInZoomControls,
// "displayZoomControls": displayZoomControls,
// "databaseEnabled": databaseEnabled,
// "domStorageEnabled": domStorageEnabled,
// "useWideViewPort": useWideViewPort,
// "safeBrowsingEnabled": safeBrowsingEnabled,
// "mixedContentMode": mixedContentMode?.toNativeValue(),
// "allowContentAccess": allowContentAccess,
// "allowFileAccess": allowFileAccess,
// "appCachePath": appCachePath,
// "blockNetworkImage": blockNetworkImage,
// "blockNetworkLoads": blockNetworkLoads,
// "cacheMode": cacheMode?.toNativeValue(),
// "cursiveFontFamily": cursiveFontFamily,
// "defaultFixedFontSize": defaultFixedFontSize,
// "defaultFontSize": defaultFontSize,
// "defaultTextEncodingName": defaultTextEncodingName,
// "disabledActionModeMenuItems":
// disabledActionModeMenuItems?.toNativeValue(),
// "fantasyFontFamily": fantasyFontFamily,
// "fixedFontFamily": fixedFontFamily,
// "forceDark": forceDark?.toNativeValue(),
// "forceDarkStrategy": forceDarkStrategy?.toNativeValue(),
// "geolocationEnabled": geolocationEnabled,
// "layoutAlgorithm": layoutAlgorithm?.toNativeValue(),
// "loadWithOverviewMode": loadWithOverviewMode,
// "loadsImagesAutomatically": loadsImagesAutomatically,
// "minimumLogicalFontSize": minimumLogicalFontSize,
// "initialScale": initialScale,
// "needInitialFocus": needInitialFocus,
// "offscreenPreRaster": offscreenPreRaster,
// "sansSerifFontFamily": sansSerifFontFamily,
// "serifFontFamily": serifFontFamily,
// "standardFontFamily": standardFontFamily,
// "saveFormData": saveFormData,
// "thirdPartyCookiesEnabled": thirdPartyCookiesEnabled,
// "hardwareAcceleration": hardwareAcceleration,
// "supportMultipleWindows": supportMultipleWindows,
// "useHybridComposition": useHybridComposition,
// "regexToCancelSubFramesLoading": regexToCancelSubFramesLoading,
// "useShouldInterceptRequest": useShouldInterceptRequest,
// "useOnRenderProcessGone": useOnRenderProcessGone,
// "overScrollMode": overScrollMode?.toNativeValue(),
// "networkAvailable": networkAvailable,
// "scrollBarStyle": scrollBarStyle?.toNativeValue(),
// "verticalScrollbarPosition": verticalScrollbarPosition?.toNativeValue(),
// "scrollBarDefaultDelayBeforeFade": scrollBarDefaultDelayBeforeFade,
// "scrollbarFadingEnabled": scrollbarFadingEnabled,
// "scrollBarFadeDuration": scrollBarFadeDuration,
// "rendererPriorityPolicy": rendererPriorityPolicy?.toMap(),
// "disableDefaultErrorPage": disableDefaultErrorPage,
// "verticalScrollbarThumbColor": verticalScrollbarThumbColor?.toHex(),
// "verticalScrollbarTrackColor": verticalScrollbarTrackColor?.toHex(),
// "horizontalScrollbarThumbColor": horizontalScrollbarThumbColor?.toHex(),
// "horizontalScrollbarTrackColor": horizontalScrollbarTrackColor?.toHex(),
// "willSuppressErrorPage": willSuppressErrorPage,
// "algorithmicDarkeningAllowed": algorithmicDarkeningAllowed,
// "requestedWithHeaderMode": requestedWithHeaderMode?.toNativeValue(),
// "enterpriseAuthenticationAppLinkPolicyEnabled":
// enterpriseAuthenticationAppLinkPolicyEnabled,
// "disallowOverScroll": disallowOverScroll,
// "enableViewportScale": enableViewportScale,
// "suppressesIncrementalRendering": suppressesIncrementalRendering,
// "allowsAirPlayForMediaPlayback": allowsAirPlayForMediaPlayback,
// "allowsBackForwardNavigationGestures":
// allowsBackForwardNavigationGestures,
// "allowsLinkPreview": allowsLinkPreview,
// "ignoresViewportScaleLimits": ignoresViewportScaleLimits,
// "allowsInlineMediaPlayback": allowsInlineMediaPlayback,
// "allowsPictureInPictureMediaPlayback":
// allowsPictureInPictureMediaPlayback,
// "isFraudulentWebsiteWarningEnabled": isFraudulentWebsiteWarningEnabled,
// "selectionGranularity": selectionGranularity.toNativeValue(),
// "dataDetectorTypes": dataDetectorTypesList,
// "sharedCookiesEnabled": sharedCookiesEnabled,
// "automaticallyAdjustsScrollIndicatorInsets":
// automaticallyAdjustsScrollIndicatorInsets,
// "accessibilityIgnoresInvertColors": accessibilityIgnoresInvertColors,
// "decelerationRate": decelerationRate.toNativeValue(),
// "alwaysBounceVertical": alwaysBounceVertical,
// "alwaysBounceHorizontal": alwaysBounceHorizontal,
// "scrollsToTop": scrollsToTop,
// "isPagingEnabled": isPagingEnabled,
// "maximumZoomScale": maximumZoomScale,
// "minimumZoomScale": minimumZoomScale,
// "contentInsetAdjustmentBehavior":
// contentInsetAdjustmentBehavior.toNativeValue(),
// "isDirectionalLockEnabled": isDirectionalLockEnabled,
// "mediaType": mediaType,
// "pageZoom": pageZoom,
// "limitsNavigationsToAppBoundDomains": limitsNavigationsToAppBoundDomains,
// "useOnNavigationResponse": useOnNavigationResponse,
// "applePayAPIEnabled": applePayAPIEnabled,
// "allowingReadAccessTo": allowingReadAccessTo.toString(),
// "disableLongPressContextMenuOnLinks": disableLongPressContextMenuOnLinks,
// "disableInputAccessoryView": disableInputAccessoryView,
// "underPageBackgroundColor": underPageBackgroundColor?.toHex(),
// "isTextInteractionEnabled": isTextInteractionEnabled,
// "isSiteSpecificQuirksModeEnabled": isSiteSpecificQuirksModeEnabled,
// "upgradeKnownHostsToHTTPS": upgradeKnownHostsToHTTPS,
// "isElementFullscreenEnabled": isElementFullscreenEnabled,
// "isFindInteractionEnabled": isFindInteractionEnabled,
// "minimumViewportInset": minimumViewportInset?.toMap(),
// "maximumViewportInset": maximumViewportInset?.toMap(),
// "iframeAllow": iframeAllow,
// "iframeAllowFullscreen": iframeAllowFullscreen,
// "iframeSandbox": iframeSandbox?.map((e) => e.toNativeValue()).toList(),
// "iframeReferrerPolicy": iframeReferrerPolicy,
// "iframeName": iframeName,
// "iframeCsp": iframeCsp,
// };
// }
//
// ///Gets a [InAppWebViewSettings] instance from a [Map] value.
// factory InAppWebViewSettings.fromMap(Map<String, dynamic> map) {
// List<ContentBlocker> contentBlockers = [];
// List<dynamic>? contentBlockersMapList = map["contentBlockers"];
// if (contentBlockersMapList != null) {
// contentBlockersMapList.forEach((contentBlocker) {
// contentBlockers.add(ContentBlocker.fromMap(
// Map<dynamic, Map<dynamic, dynamic>>.from(
// Map<dynamic, dynamic>.from(contentBlocker))));
// });
// }
// List<DataDetectorTypes> dataDetectorTypes = [];
// List<String> dataDetectorTypesList =
// List<String>.from(map["dataDetectorTypes"] ?? []);
// dataDetectorTypesList.forEach((dataDetectorTypeValue) {
// var dataDetectorType =
// DataDetectorTypes.fromNativeValue(dataDetectorTypeValue);
// if (dataDetectorType != null) {
// dataDetectorTypes.add(dataDetectorType);
// }
// });
//
// var settings = InAppWebViewSettings();
// settings.useShouldOverrideUrlLoading = map["useShouldOverrideUrlLoading"];
// settings.useOnLoadResource = map["useOnLoadResource"];
// settings.useOnDownloadStart = map["useOnDownloadStart"];
// settings.clearCache = map["clearCache"];
// settings.userAgent = map["userAgent"];
// settings.applicationNameForUserAgent = map["applicationNameForUserAgent"];
// settings.javaScriptEnabled = map["javaScriptEnabled"];
// settings.javaScriptCanOpenWindowsAutomatically =
// map["javaScriptCanOpenWindowsAutomatically"];
// settings.mediaPlaybackRequiresUserGesture =
// map["mediaPlaybackRequiresUserGesture"];
// settings.verticalScrollBarEnabled = map["verticalScrollBarEnabled"];
// settings.horizontalScrollBarEnabled = map["horizontalScrollBarEnabled"];
// settings.resourceCustomSchemes =
// List<String>.from(map["resourceCustomSchemes"] ?? []);
// settings.contentBlockers = contentBlockers;
// settings.preferredContentMode =
// UserPreferredContentMode.fromNativeValue(map["preferredContentMode"]);
// settings.useShouldInterceptAjaxRequest =
// map["useShouldInterceptAjaxRequest"];
// settings.useShouldInterceptFetchRequest =
// map["useShouldInterceptFetchRequest"];
// settings.incognito = map["incognito"];
// settings.cacheEnabled = map["cacheEnabled"];
// settings.transparentBackground = map["transparentBackground"];
// settings.disableVerticalScroll = map["disableVerticalScroll"];
// settings.disableHorizontalScroll = map["disableHorizontalScroll"];
// settings.disableContextMenu = map["disableContextMenu"];
// settings.supportZoom = map["supportZoom"];
// settings.allowFileAccessFromFileURLs = map["allowFileAccessFromFileURLs"];
// settings.allowUniversalAccessFromFileURLs =
// map["allowUniversalAccessFromFileURLs"];
// if (kIsWeb) {
// settings.iframeAllow = map["iframeAllow"];
// settings.iframeAllowFullscreen = map["iframeAllowFullscreen"];
// settings.iframeSandbox = map["iframeSandbox"] != null
// ? Set.from((map["iframeSandbox"].cast<String>() as List<String>)
// .map((e) => Sandbox.fromNativeValue(e)))
// : null;
// settings.iframeReferrerPolicy =
// ReferrerPolicy.fromNativeValue(map["iframeReferrerPolicy"]);
// settings.iframeName = map["iframeName"];
// settings.iframeCsp = map["iframeCsp"];
// } else {
// if (defaultTargetPlatform == TargetPlatform.android) {
// settings.textZoom = map["textZoom"];
// settings.clearSessionCache = map["clearSessionCache"];
// settings.builtInZoomControls = map["builtInZoomControls"];
// settings.displayZoomControls = map["displayZoomControls"];
// settings.databaseEnabled = map["databaseEnabled"];
// settings.domStorageEnabled = map["domStorageEnabled"];
// settings.useWideViewPort = map["useWideViewPort"];
// settings.safeBrowsingEnabled = map["safeBrowsingEnabled"];
// settings.mixedContentMode =
// MixedContentMode.fromNativeValue(map["mixedContentMode"]);
// settings.allowContentAccess = map["allowContentAccess"];
// settings.allowFileAccess = map["allowFileAccess"];
// settings.appCachePath = map["appCachePath"];
// settings.blockNetworkImage = map["blockNetworkImage"];
// settings.blockNetworkLoads = map["blockNetworkLoads"];
// settings.cacheMode = CacheMode.fromNativeValue(map["cacheMode"]);
// settings.cursiveFontFamily = map["cursiveFontFamily"];
// settings.defaultFixedFontSize = map["defaultFixedFontSize"];
// settings.defaultFontSize = map["defaultFontSize"];
// settings.defaultTextEncodingName = map["defaultTextEncodingName"];
// settings.disabledActionModeMenuItems =
// ActionModeMenuItem.fromNativeValue(
// map["disabledActionModeMenuItems"]);
// settings.fantasyFontFamily = map["fantasyFontFamily"];
// settings.fixedFontFamily = map["fixedFontFamily"];
// settings.forceDark = ForceDark.fromNativeValue(map["forceDark"]);
// settings.forceDarkStrategy =
// ForceDarkStrategy.fromNativeValue(map["forceDarkStrategy"]);
// settings.geolocationEnabled = map["geolocationEnabled"];
// settings.layoutAlgorithm =
// LayoutAlgorithm.fromNativeValue(map["layoutAlgorithm"]);
// settings.loadWithOverviewMode = map["loadWithOverviewMode"];
// settings.loadsImagesAutomatically = map["loadsImagesAutomatically"];
// settings.minimumLogicalFontSize = map["minimumLogicalFontSize"];
// settings.initialScale = map["initialScale"];
// settings.needInitialFocus = map["needInitialFocus"];
// settings.offscreenPreRaster = map["offscreenPreRaster"];
// settings.sansSerifFontFamily = map["sansSerifFontFamily"];
// settings.serifFontFamily = map["serifFontFamily"];
// settings.standardFontFamily = map["standardFontFamily"];
// settings.saveFormData = map["saveFormData"];
// settings.thirdPartyCookiesEnabled = map["thirdPartyCookiesEnabled"];
// settings.hardwareAcceleration = map["hardwareAcceleration"];
// settings.supportMultipleWindows = map["supportMultipleWindows"];
// settings.regexToCancelSubFramesLoading =
// map["regexToCancelSubFramesLoading"];
// settings.useHybridComposition = map["useHybridComposition"];
// settings.useShouldInterceptRequest = map["useShouldInterceptRequest"];
// settings.useOnRenderProcessGone = map["useOnRenderProcessGone"];
// settings.overScrollMode =
// OverScrollMode.fromNativeValue(map["overScrollMode"]);
// settings.networkAvailable = map["networkAvailable"];
// settings.scrollBarStyle =
// ScrollBarStyle.fromNativeValue(map["scrollBarStyle"]);
// settings.verticalScrollbarPosition =
// VerticalScrollbarPosition.fromNativeValue(
// map["verticalScrollbarPosition"]);
// settings.scrollBarDefaultDelayBeforeFade =
// map["scrollBarDefaultDelayBeforeFade"];
// settings.scrollbarFadingEnabled = map["scrollbarFadingEnabled"];
// settings.scrollBarFadeDuration = map["scrollBarFadeDuration"];
// settings.rendererPriorityPolicy = RendererPriorityPolicy.fromMap(
// map["rendererPriorityPolicy"]?.cast<String, dynamic>());
// settings.disableDefaultErrorPage = map["disableDefaultErrorPage"];
// settings.verticalScrollbarThumbColor =
// UtilColor.fromHex(map["verticalScrollbarThumbColor"]);
// settings.verticalScrollbarTrackColor =
// UtilColor.fromHex(map["verticalScrollbarTrackColor"]);
// settings.horizontalScrollbarThumbColor =
// UtilColor.fromHex(map["horizontalScrollbarThumbColor"]);
// settings.horizontalScrollbarTrackColor =
// UtilColor.fromHex(map["horizontalScrollbarTrackColor"]);
// settings.willSuppressErrorPage = map["willSuppressErrorPage"];
// settings.algorithmicDarkeningAllowed =
// map["algorithmicDarkeningAllowed"];
// settings.requestedWithHeaderMode =
// RequestedWithHeaderMode.fromNativeValue(
// map["requestedWithHeaderMode"]);
// settings.enterpriseAuthenticationAppLinkPolicyEnabled =
// map["enterpriseAuthenticationAppLinkPolicyEnabled"];
// } else if (defaultTargetPlatform == TargetPlatform.iOS ||
// defaultTargetPlatform == TargetPlatform.macOS) {
// settings.disallowOverScroll = map["disallowOverScroll"];
// settings.enableViewportScale = map["enableViewportScale"];
// settings.suppressesIncrementalRendering =
// map["suppressesIncrementalRendering"];
// settings.allowsAirPlayForMediaPlayback =
// map["allowsAirPlayForMediaPlayback"];
// settings.allowsBackForwardNavigationGestures =
// map["allowsBackForwardNavigationGestures"];
// settings.allowsLinkPreview = map["allowsLinkPreview"];
// settings.ignoresViewportScaleLimits = map["ignoresViewportScaleLimits"];
// settings.allowsInlineMediaPlayback = map["allowsInlineMediaPlayback"];
// settings.allowsPictureInPictureMediaPlayback =
// map["allowsPictureInPictureMediaPlayback"];
// settings.isFraudulentWebsiteWarningEnabled =
// map["isFraudulentWebsiteWarningEnabled"];
// settings.selectionGranularity =
// SelectionGranularity.fromNativeValue(map["selectionGranularity"])!;
// settings.dataDetectorTypes = dataDetectorTypes;
// settings.sharedCookiesEnabled = map["sharedCookiesEnabled"];
// settings.automaticallyAdjustsScrollIndicatorInsets =
// map["automaticallyAdjustsScrollIndicatorInsets"];
// settings.accessibilityIgnoresInvertColors =
// map["accessibilityIgnoresInvertColors"];
// settings.decelerationRate = ScrollViewDecelerationRate.fromNativeValue(
// map["decelerationRate"])!;
// settings.alwaysBounceVertical = map["alwaysBounceVertical"];
// settings.alwaysBounceHorizontal = map["alwaysBounceHorizontal"];
// settings.scrollsToTop = map["scrollsToTop"];
// settings.isPagingEnabled = map["isPagingEnabled"];
// settings.maximumZoomScale = map["maximumZoomScale"];
// settings.minimumZoomScale = map["minimumZoomScale"];
// settings.contentInsetAdjustmentBehavior =
// ScrollViewContentInsetAdjustmentBehavior.fromNativeValue(
// map["contentInsetAdjustmentBehavior"])!;
// settings.isDirectionalLockEnabled = map["isDirectionalLockEnabled"];
// settings.mediaType = map["mediaType"];
// settings.pageZoom = map["pageZoom"];
// settings.limitsNavigationsToAppBoundDomains =
// map["limitsNavigationsToAppBoundDomains"];
// settings.useOnNavigationResponse = map["useOnNavigationResponse"];
// settings.applePayAPIEnabled = map["applePayAPIEnabled"];
// settings.allowingReadAccessTo = map["allowingReadAccessTo"] != null
// ? Uri.parse(map["allowingReadAccessTo"])
// : null;
// settings.disableLongPressContextMenuOnLinks =
// map["disableLongPressContextMenuOnLinks"];
// settings.disableInputAccessoryView = map["disableInputAccessoryView"];
// settings.underPageBackgroundColor =
// UtilColor.fromHex(map["underPageBackgroundColor"]);
// settings.isTextInteractionEnabled = map["isTextInteractionEnabled"];
// settings.isSiteSpecificQuirksModeEnabled =
// map["isSiteSpecificQuirksModeEnabled"];
// settings.upgradeKnownHostsToHTTPS = map["upgradeKnownHostsToHTTPS"];
// settings.isElementFullscreenEnabled = map["isElementFullscreenEnabled"];
// settings.isFindInteractionEnabled = map["isFindInteractionEnabled"];
// settings.minimumViewportInset = MapEdgeInsets.fromMap(
// map["minimumViewportInset"]?.cast<String, double>());
// settings.maximumViewportInset = MapEdgeInsets.fromMap(
// map["maximumViewportInset"]?.cast<String, double>());
// }
// }
// return settings;
// }
}
///Class that represents the options that can be used for a [WebView].

File diff suppressed because one or more lines are too long

View File

@ -440,8 +440,9 @@ class PrintJobSettings {
showsProgressPanel: map["showsProgressPanel"],
jobDisposition:
PrintJobDisposition.fromNativeValue(map["jobDisposition"]),
jobSavingURL:
map["jobSavingURL"] != null ? Uri.parse(map["jobSavingURL"]) : null,
jobSavingURL: map["jobSavingURL"] != null
? Uri.tryParse(map["jobSavingURL"])
: null,
paperName: map["paperName"],
horizontalPagination:
PrintJobPaginationMode.fromNativeValue(map["horizontalPagination"]),

View File

@ -103,7 +103,7 @@ class AjaxRequest {
final instance = AjaxRequest(
data: map['data'],
method: map['method'],
url: map['url'] != null ? Uri.parse(map['url']) : null,
url: map['url'] != null ? Uri.tryParse(map['url']) : null,
isAsync: map['isAsync'],
user: map['user'],
password: map['password'],
@ -113,7 +113,7 @@ class AjaxRequest {
readyState: AjaxRequestReadyState.fromNativeValue(map['readyState']),
status: map['status'],
responseURL:
map['responseURL'] != null ? Uri.parse(map['responseURL']) : null,
map['responseURL'] != null ? Uri.tryParse(map['responseURL']) : null,
responseType: map['responseType'],
response: map['response'],
responseText: map['responseText'],

View File

@ -43,7 +43,7 @@ class DownloadStartRequest {
return null;
}
final instance = DownloadStartRequest(
url: Uri.parse(map['url']),
url: (Uri.tryParse(map['url']) ?? Uri()),
userAgent: map['userAgent'],
contentDisposition: map['contentDisposition'],
mimeType: map['mimeType'],

View File

@ -27,7 +27,7 @@ class Favicon {
return null;
}
final instance = Favicon(
url: Uri.parse(map['url']),
url: (Uri.tryParse(map['url']) ?? Uri()),
rel: map['rel'],
width: map['width'],
height: map['height'],

View File

@ -67,7 +67,7 @@ class FetchRequest {
return null;
}
final instance = FetchRequest(
url: map['url'] != null ? Uri.parse(map['url']) : null,
url: map['url'] != null ? Uri.tryParse(map['url']) : null,
method: map['method'],
headers: map['headers']?.cast<String, dynamic>(),
body: map['body'],

View File

@ -41,7 +41,7 @@ class FetchRequestFederatedCredential extends FetchRequestCredential {
name: map['name'],
protocol: map['protocol'],
provider: map['provider'],
iconURL: map['iconURL'] != null ? Uri.parse(map['iconURL']) : null,
iconURL: map['iconURL'] != null ? Uri.tryParse(map['iconURL']) : null,
);
instance.type = map['type'];
return instance;

View File

@ -32,7 +32,7 @@ class FetchRequestPasswordCredential extends FetchRequestCredential {
id: map['id'],
name: map['name'],
password: map['password'],
iconURL: map['iconURL'] != null ? Uri.parse(map['iconURL']) : null,
iconURL: map['iconURL'] != null ? Uri.tryParse(map['iconURL']) : null,
);
instance.type = map['type'];
return instance;

View File

@ -46,11 +46,11 @@ class InAppWebViewInitialData {
}
final instance = InAppWebViewInitialData(
data: map['data'],
baseUrl: map['baseUrl'] != null ? Uri.parse(map['baseUrl']) : null,
baseUrl: map['baseUrl'] != null ? Uri.tryParse(map['baseUrl']) : null,
androidHistoryUrl:
map['historyUrl'] != null ? Uri.parse(map['historyUrl']) : null,
map['historyUrl'] != null ? Uri.tryParse(map['historyUrl']) : null,
historyUrl:
map['historyUrl'] != null ? Uri.parse(map['historyUrl']) : null,
map['historyUrl'] != null ? Uri.tryParse(map['historyUrl']) : null,
);
instance.mimeType = map['mimeType'];
instance.encoding = map['encoding'];

View File

@ -37,7 +37,7 @@ class JsAlertRequest {
return null;
}
final instance = JsAlertRequest(
url: map['url'] != null ? Uri.parse(map['url']) : null,
url: map['url'] != null ? Uri.tryParse(map['url']) : null,
message: map['message'],
iosIsMainFrame: map['isMainFrame'],
isMainFrame: map['isMainFrame'],

View File

@ -21,7 +21,7 @@ class JsBeforeUnloadRequest {
return null;
}
final instance = JsBeforeUnloadRequest(
url: map['url'] != null ? Uri.parse(map['url']) : null,
url: map['url'] != null ? Uri.tryParse(map['url']) : null,
message: map['message'],
);
return instance;

View File

@ -37,7 +37,7 @@ class JsConfirmRequest {
return null;
}
final instance = JsConfirmRequest(
url: map['url'] != null ? Uri.parse(map['url']) : null,
url: map['url'] != null ? Uri.tryParse(map['url']) : null,
message: map['message'],
iosIsMainFrame: map['isMainFrame'],
isMainFrame: map['isMainFrame'],

View File

@ -41,7 +41,7 @@ class JsPromptRequest {
return null;
}
final instance = JsPromptRequest(
url: map['url'] != null ? Uri.parse(map['url']) : null,
url: map['url'] != null ? Uri.tryParse(map['url']) : null,
message: map['message'],
defaultValue: map['defaultValue'],
iosIsMainFrame: map['isMainFrame'],

View File

@ -29,7 +29,7 @@ class LoadedResource {
}
final instance = LoadedResource(
initiatorType: map['initiatorType'],
url: map['url'] != null ? Uri.parse(map['url']) : null,
url: map['url'] != null ? Uri.tryParse(map['url']) : null,
startTime: map['startTime'],
duration: map['duration'],
);

View File

@ -28,7 +28,7 @@ class PermissionRequest {
return null;
}
final instance = PermissionRequest(
origin: Uri.parse(map['origin']),
origin: (Uri.tryParse(map['origin']) ?? Uri()),
frame: FrameInfo.fromMap(map['frame']?.cast<String, dynamic>()),
);
instance.resources = List<PermissionResourceType>.from(map['resources']

View File

@ -273,8 +273,9 @@ class PrintJobAttributes {
isVerticallyCentered: map['isVerticallyCentered'],
isSelectionOnly: map['isSelectionOnly'],
scalingFactor: map['scalingFactor'],
jobSavingURL:
map['jobSavingURL'] != null ? Uri.parse(map['jobSavingURL']) : null,
jobSavingURL: map['jobSavingURL'] != null
? Uri.tryParse(map['jobSavingURL'])
: null,
detailedErrorReporting: map['detailedErrorReporting'],
faxNumber: map['faxNumber'],
headerAndFooter: map['headerAndFooter'],

View File

@ -24,7 +24,7 @@ class RequestFocusNodeHrefResult {
return null;
}
final instance = RequestFocusNodeHrefResult(
url: map['url'] != null ? Uri.parse(map['url']) : null,
url: map['url'] != null ? Uri.tryParse(map['url']) : null,
title: map['title'],
src: map['src'],
);

View File

@ -18,7 +18,7 @@ class RequestImageRefResult {
return null;
}
final instance = RequestImageRefResult(
url: map['url'] != null ? Uri.parse(map['url']) : null,
url: map['url'] != null ? Uri.tryParse(map['url']) : null,
);
return instance;
}

View File

@ -186,7 +186,7 @@ class URLRequest {
return null;
}
final instance = URLRequest(
url: map['url'] != null ? Uri.parse(map['url']) : null,
url: map['url'] != null ? Uri.tryParse(map['url']) : null,
method: map['method'],
body: map['body'],
headers: map['headers']?.cast<String, String>(),
@ -210,10 +210,10 @@ class URLRequest {
iosTimeoutInterval: map['timeoutInterval'],
timeoutInterval: map['timeoutInterval'],
iosMainDocumentURL: map['mainDocumentURL'] != null
? Uri.parse(map['mainDocumentURL'])
? Uri.tryParse(map['mainDocumentURL'])
: null,
mainDocumentURL: map['mainDocumentURL'] != null
? Uri.parse(map['mainDocumentURL'])
? Uri.tryParse(map['mainDocumentURL'])
: null,
assumesHTTP3Capable: map['assumesHTTP3Capable'],
attribution: URLRequestAttribution.fromNativeValue(map['attribution']),

View File

@ -43,7 +43,7 @@ class URLResponse {
return null;
}
final instance = URLResponse(
url: map['url'] != null ? Uri.parse(map['url']) : null,
url: map['url'] != null ? Uri.tryParse(map['url']) : null,
expectedContentLength: map['expectedContentLength'],
mimeType: map['mimeType'],
suggestedFilename: map['suggestedFilename'],
@ -116,7 +116,7 @@ class IOSURLResponse {
return null;
}
final instance = IOSURLResponse(
url: map['url'] != null ? Uri.parse(map['url']) : null,
url: map['url'] != null ? Uri.tryParse(map['url']) : null,
expectedContentLength: map['expectedContentLength'],
mimeType: map['mimeType'],
suggestedFilename: map['suggestedFilename'],

View File

@ -33,9 +33,9 @@ class WebHistoryItem {
}
final instance = WebHistoryItem(
originalUrl:
map['originalUrl'] != null ? Uri.parse(map['originalUrl']) : null,
map['originalUrl'] != null ? Uri.tryParse(map['originalUrl']) : null,
title: map['title'],
url: map['url'] != null ? Uri.parse(map['url']) : null,
url: map['url'] != null ? Uri.tryParse(map['url']) : null,
index: map['index'],
offset: map['offset'],
);

View File

@ -52,7 +52,7 @@ class WebResourceRequest {
return null;
}
final instance = WebResourceRequest(
url: Uri.parse(map['url']),
url: (Uri.tryParse(map['url']) ?? Uri()),
headers: map['headers']?.cast<String, String>(),
method: map['method'],
hasGesture: map['hasGesture'],

View File

@ -10,10 +10,10 @@ part 'web_resource_response.g.dart';
@ExchangeableObject()
class WebResourceResponse_ {
///The resource response's MIME type, for example `text/html`.
String contentType;
String? contentType;
///The resource response's encoding. The default value is `utf-8`.
String contentEncoding;
String? contentEncoding;
///The data provided by the resource response.
Uint8List? data;

View File

@ -9,10 +9,10 @@ part of 'web_resource_response.dart';
///Class representing a resource response of the [WebView].
class WebResourceResponse {
///The resource response's MIME type, for example `text/html`.
String contentType;
String? contentType;
///The resource response's encoding. The default value is `utf-8`.
String contentEncoding;
String? contentEncoding;
///The data provided by the resource response.
Uint8List? data;

View File

@ -344,7 +344,7 @@ class InAppWebViewWebElement implements Disposable {
{required String url, required Uint8List postData}) async {
await loadUrl(
urlRequest:
URLRequest(url: Uri.parse(url), method: "POST", body: postData));
URLRequest(url: Uri.tryParse(url), method: "POST", body: postData));
}
Future<void> injectJavascriptFileFromUrl(

View File

@ -129,7 +129,7 @@ class WebAuthenticationSession implements Disposable {
switch (call.method) {
case "onComplete":
String? url = call.arguments["url"];
Uri? uri = url != null ? Uri.parse(url) : null;
Uri? uri = url != null ? Uri.tryParse(url) : null;
var error = WebAuthenticationSessionError.fromNativeValue(
call.arguments["errorCode"]);
if (onComplete != null) {

View File

@ -60,7 +60,7 @@ class WebMessageListener {
if (onPostMessage != null) {
String? message = call.arguments["message"];
Uri? sourceOrigin = call.arguments["sourceOrigin"] != null
? Uri.parse(call.arguments["sourceOrigin"])
? Uri.tryParse(call.arguments["sourceOrigin"])
: null;
bool isMainFrame = call.arguments["isMainFrame"];
onPostMessage!(message, sourceOrigin, isMainFrame, _replyProxy!);

View File

@ -1,6 +1,6 @@
name: flutter_inappwebview
description: A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window.
version: 6.0.0-beta.5
version: 6.0.0-beta.6
homepage: https://inappwebview.dev/
repository: https://github.com/pichillilorenzo/flutter_inappwebview
issue_tracker: https://github.com/pichillilorenzo/flutter_inappwebview/issues