Added useOnNavigationResponse iOS-specific WebView option, Added iosOnNavigationResponse iOS-specific WebView event, Added new iOS-specific attributes to ShouldOverrideUrlLoadingRequest and CreateWindowRequest classes

This commit is contained in:
Lorenzo Pichilli 2021-02-10 00:15:10 +01:00
parent 6ee1e6f873
commit 2aab462702
13 changed files with 612 additions and 35 deletions

View File

@ -9,12 +9,14 @@
- Added `initialUserScripts` WebView option
- Added `addUserScript`, `addUserScripts`, `removeUserScript`, `removeUserScripts`, `removeAllUserScripts`, `callAsyncJavaScript` WebView methods
- Added `contentWorld` argument to `evaluateJavascript` WebView method
- Added `isDirectionalLockEnabled`, `mediaType`, `pageZoom`, `limitsNavigationsToAppBoundDomains` iOS-specific WebView options
- Added `isDirectionalLockEnabled`, `mediaType`, `pageZoom`, `limitsNavigationsToAppBoundDomains`, `useOnNavigationResponse` iOS-specific WebView options
- Added `handlesURLScheme`, `createPdf`, `createWebArchiveData` iOS-specific WebView methods
- Added `iosOnNavigationResponse` iOS-specific WebView events
- Added `iosAnimated` optional argument to `zoomBy` WebView method
- Added `screenshotConfiguration` optional argument to `takeScreenshot` WebView method
- Added `scriptHtmlTagAttributes` optional argument to `injectJavascriptFileFromUrl` WebView method
- Added `cssLinkHtmlTagAttributes` optional argument to `injectCSSFileFromUrl` WebView method
- Added new iOS-specific attributes to `ShouldOverrideUrlLoadingRequest` and `CreateWindowRequest` classes
- Updated integration tests
- Merge "Upgraded appcompat to 1.2.0-rc-02" [#465](https://github.com/pichillilorenzo/flutter_inappwebview/pull/465) (thanks to [andreidiaconu](https://github.com/andreidiaconu))
- Merge "Added missing field 'headers' which returned by WebResourceResponse.toMap()" [#490](https://github.com/pichillilorenzo/flutter_inappwebview/pull/490) (thanks to [Doflatango](https://github.com/Doflatango))

View File

@ -662,6 +662,7 @@ Instead, on the `onLoadStop` WebView event, you can use `callHandler` directly:
* `selectionGranularity`: The level of granularity with which the user can interactively select content in the web view.
* `sharedCookiesEnabled`: Set `true` if shared cookies from `HTTPCookieStorage.shared` should used for every load request in the WebView.
* `suppressesIncrementalRendering`: Set to `true` if you want the WebView suppresses content rendering until it is fully loaded into memory. The default value is `false`.
* `useOnNavigationResponse`: Set to `true` to be able to listen at the `iosOnNavigationResponse` event. The default value is `false`.
#### `InAppWebView` Events

View File

@ -586,6 +586,14 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
obj.put("androidIsUserGesture", isUserGesture);
obj.put("iosWKNavigationType", null);
obj.put("iosIsForMainFrame", null);
obj.put("iosAllowsCellularAccess", null);
obj.put("iosAllowsConstrainedNetworkAccess", null);
obj.put("iosAllowsExpensiveNetworkAccess", null);
obj.put("iosCachePolicy", null);
obj.put("iosHttpShouldHandleCookies", null);
obj.put("iosHttpShouldUsePipelining", null);
obj.put("iosNetworkServiceType", null);
obj.put("iosTimeoutInterval", null);
windowWebViewMessages.put(windowId, resultMsg);

View File

@ -120,6 +120,15 @@ public class InAppWebViewClient extends WebViewClient {
obj.put("androidHasGesture", hasGesture);
obj.put("androidIsRedirect", isRedirect);
obj.put("iosWKNavigationType", null);
obj.put("iosAllowsCellularAccess", null);
obj.put("iosAllowsConstrainedNetworkAccess", null);
obj.put("iosAllowsExpensiveNetworkAccess", null);
obj.put("iosCachePolicy", null);
obj.put("iosHttpShouldHandleCookies", null);
obj.put("iosHttpShouldUsePipelining", null);
obj.put("iosNetworkServiceType", null);
obj.put("iosTimeoutInterval", null);
channel.invokeMethod("shouldOverrideUrlLoading", obj, new MethodChannel.Result() {
@Override
public void success(Object response) {

View File

@ -2402,17 +2402,24 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
return result;
}
@available(iOS 13.0, *)
public func webView(_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
preferences: WKWebpagePreferences,
decisionHandler: @escaping (WKNavigationActionPolicy, WKWebpagePreferences) -> Void) {
self.webView(webView, decidePolicyFor: navigationAction, decisionHandler: {(navigationActionPolicy) -> Void in
decisionHandler(navigationActionPolicy, preferences)
})
}
public func webView(_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if let url = navigationAction.request.url {
if navigationAction.request.url != nil {
if activateShouldOverrideUrlLoading && (options?.useShouldOverrideUrlLoading)! {
let isForMainFrame = navigationAction.targetFrame?.isMainFrame ?? false
shouldOverrideUrlLoading(url: url, method: navigationAction.request.httpMethod, headers: navigationAction.request.allHTTPHeaderFields, isForMainFrame: isForMainFrame, navigationType: navigationAction.navigationType, result: { (result) -> Void in
if activateShouldOverrideUrlLoading, let useShouldOverrideUrlLoading = options?.useShouldOverrideUrlLoading, useShouldOverrideUrlLoading {
shouldOverrideUrlLoading(navigationAction: navigationAction, result: { (result) -> Void in
if result is FlutterError {
print((result as! FlutterError).message ?? "")
decisionHandler(.allow)
@ -2475,18 +2482,55 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
}
}
if (options?.useOnDownloadStart)! {
let useOnNavigationResponse = options?.useOnNavigationResponse
if useOnNavigationResponse != nil, useOnNavigationResponse! {
onNavigationResponse(navigationResponse: navigationResponse, result: { (result) -> Void in
if result is FlutterError {
print((result as! FlutterError).message ?? "")
decisionHandler(.allow)
return
}
else if (result as? NSObject) == FlutterMethodNotImplemented {
decisionHandler(.allow)
return
}
else {
var response: [String: Any]
if let r = result {
response = r as! [String: Any]
var action = response["action"] as? Int
action = action != nil ? action : 0;
switch action {
case 1:
decisionHandler(.allow)
break
default:
decisionHandler(.cancel)
}
return;
}
decisionHandler(.allow)
}
})
}
if let useOnDownloadStart = options?.useOnDownloadStart, useOnDownloadStart {
let mimeType = navigationResponse.response.mimeType
if let url = navigationResponse.response.url, navigationResponse.isForMainFrame {
if mimeType != nil && !mimeType!.starts(with: "text/") {
onDownloadStart(url: url.absoluteString)
decisionHandler(.cancel)
if useOnNavigationResponse == nil || !useOnNavigationResponse! {
decisionHandler(.cancel)
}
return
}
}
}
decisionHandler(.allow)
if useOnNavigationResponse == nil || !useOnNavigationResponse! {
decisionHandler(.allow)
}
}
public func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
@ -2758,7 +2802,6 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
}
return identityAndTrust;
}
func createAlertDialog(message: String?, responseMessage: String?, confirmButtonTitle: String?, completionHandler: @escaping () -> Void) {
let title = responseMessage != nil && !responseMessage!.isEmpty ? responseMessage : message
@ -3008,13 +3051,28 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
InAppWebView.windowWebViews[windowId] = webViewTransport
windowWebView.stopLoading()
var iosAllowsConstrainedNetworkAccess: Bool? = nil
var iosAllowsExpensiveNetworkAccess: Bool? = nil
if #available(iOS 13.0, *) {
iosAllowsConstrainedNetworkAccess = navigationAction.request.allowsConstrainedNetworkAccess
iosAllowsExpensiveNetworkAccess = navigationAction.request.allowsExpensiveNetworkAccess
}
let arguments: [String: Any?] = [
"url": navigationAction.request.url?.absoluteString,
"windowId": windowId,
"androidIsDialog": nil,
"androidIsUserGesture": nil,
"iosWKNavigationType": navigationAction.navigationType.rawValue,
"iosIsForMainFrame": navigationAction.targetFrame?.isMainFrame ?? false
"iosIsForMainFrame": navigationAction.targetFrame?.isMainFrame ?? false,
"iosAllowsCellularAccess": navigationAction.request.allowsCellularAccess,
"iosAllowsConstrainedNetworkAccess": iosAllowsConstrainedNetworkAccess,
"iosAllowsExpensiveNetworkAccess": iosAllowsExpensiveNetworkAccess,
"iosCachePolicy": navigationAction.request.cachePolicy.rawValue,
"iosHttpShouldHandleCookies": navigationAction.request.httpShouldHandleCookies,
"iosHttpShouldUsePipelining": navigationAction.request.httpShouldUsePipelining,
"iosNetworkServiceType": navigationAction.request.networkServiceType.rawValue,
"iosTimeoutInterval": navigationAction.request.timeoutInterval,
]
channel?.invokeMethod("onCreateWindow", arguments: arguments, result: { (result) -> Void in
if result is FlutterError {
@ -3218,19 +3276,46 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
channel?.invokeMethod("onLoadResourceCustomScheme", arguments: arguments, result: result)
}
public func shouldOverrideUrlLoading(url: URL, method: String?, headers: [String: String]?, isForMainFrame: Bool, navigationType: WKNavigationType, result: FlutterResult?) {
public func shouldOverrideUrlLoading(navigationAction: WKNavigationAction, result: FlutterResult?) {
var iosAllowsConstrainedNetworkAccess: Bool? = nil
var iosAllowsExpensiveNetworkAccess: Bool? = nil
if #available(iOS 13.0, *) {
iosAllowsConstrainedNetworkAccess = navigationAction.request.allowsConstrainedNetworkAccess
iosAllowsExpensiveNetworkAccess = navigationAction.request.allowsExpensiveNetworkAccess
}
let arguments: [String: Any?] = [
"url": url.absoluteString,
"method": method,
"headers": headers,
"isForMainFrame": isForMainFrame,
"url": navigationAction.request.url?.absoluteString,
"method": navigationAction.request.httpMethod,
"headers": navigationAction.request.allHTTPHeaderFields,
"isForMainFrame": navigationAction.targetFrame?.isMainFrame ?? false,
"androidHasGesture": nil,
"androidIsRedirect": nil,
"iosWKNavigationType": navigationType.rawValue
"iosWKNavigationType": navigationAction.navigationType.rawValue,
"iosAllowsCellularAccess": navigationAction.request.allowsCellularAccess,
"iosAllowsConstrainedNetworkAccess": iosAllowsConstrainedNetworkAccess,
"iosAllowsExpensiveNetworkAccess": iosAllowsExpensiveNetworkAccess,
"iosCachePolicy": navigationAction.request.cachePolicy.rawValue,
"iosHttpShouldHandleCookies": navigationAction.request.httpShouldHandleCookies,
"iosHttpShouldUsePipelining": navigationAction.request.httpShouldUsePipelining,
"iosNetworkServiceType": navigationAction.request.networkServiceType.rawValue,
"iosTimeoutInterval": navigationAction.request.timeoutInterval,
]
channel?.invokeMethod("shouldOverrideUrlLoading", arguments: arguments, result: result)
}
public func onNavigationResponse(navigationResponse: WKNavigationResponse, result: FlutterResult?) {
let arguments: [String: Any?] = [
"url": navigationResponse.response.url?.absoluteString,
"isForMainFrame": navigationResponse.isForMainFrame,
"canShowMIMEType": navigationResponse.canShowMIMEType,
"expectedContentLength": navigationResponse.response.expectedContentLength,
"mimeType": navigationResponse.response.mimeType,
"suggestedFilename": navigationResponse.response.suggestedFilename,
"textEncodingName": navigationResponse.response.textEncodingName,
]
channel?.invokeMethod("onNavigationResponse", arguments: arguments, result: result)
}
public func onReceivedHttpAuthRequest(challenge: URLAuthenticationChallenge, result: FlutterResult?) {
let arguments: [String: Any?] = [
"host": challenge.protectionSpace.host,

View File

@ -65,6 +65,7 @@ public class InAppWebViewOptions: Options<InAppWebView> {
var mediaType: String? = nil
var pageZoom = 1.0
var limitsNavigationsToAppBoundDomains = false
var useOnNavigationResponse = false
override init(){
super.init()

View File

@ -77,6 +77,7 @@ class HeadlessInAppWebView implements WebView {
this.androidOnReceivedLoginRequest,
this.iosOnWebContentProcessDidTerminate,
this.iosOnDidReceiveServerRedirectForProvisionalNavigation,
this.iosOnNavigationResponse,
this.initialUrl,
this.initialFile,
this.initialData,
@ -190,6 +191,11 @@ class HeadlessInAppWebView implements WebView {
final void Function(InAppWebViewController controller)?
iosOnWebContentProcessDidTerminate;
@override
final Future<IOSNavigationResponseAction?> Function(InAppWebViewController controller,
IOSNavigationResponse navigationResponse)?
iosOnNavigationResponse;
@override
final Future<AjaxRequestAction> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest)?

View File

@ -753,6 +753,16 @@ class InAppBrowser {
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455627-webview
void iosOnDidReceiveServerRedirectForProvisionalNavigation() {}
///Called when a web view asks for permission to navigate to new content after the response to the navigation request is known.
///
///[navigationResponse] represents the navigation response.
///
///**NOTE**: available only on iOS.
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview
Future<IOSNavigationResponseAction?>?
iosOnNavigationResponse(IOSNavigationResponse navigationResponse) {}
void throwIsAlreadyOpened({String message = ''}) {
if (this.isOpened()) {
throw Exception([

View File

@ -89,6 +89,7 @@ class InAppWebView extends StatefulWidget implements WebView {
this.androidOnReceivedLoginRequest,
this.iosOnWebContentProcessDidTerminate,
this.iosOnDidReceiveServerRedirectForProvisionalNavigation,
this.iosOnNavigationResponse,
this.gestureRecognizers,
}) : super(key: key);
@ -151,6 +152,11 @@ class InAppWebView extends StatefulWidget implements WebView {
final void Function(InAppWebViewController controller)?
iosOnWebContentProcessDidTerminate;
@override
final Future<IOSNavigationResponseAction?> Function(InAppWebViewController controller,
IOSNavigationResponse navigationResponse)?
iosOnNavigationResponse;
@override
final Future<AjaxRequestAction> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest)?

View File

@ -146,6 +146,14 @@ class InAppWebViewController {
bool? androidHasGesture = call.arguments["androidHasGesture"];
bool? androidIsRedirect = call.arguments["androidIsRedirect"];
int? iosWKNavigationType = call.arguments["iosWKNavigationType"];
bool? iosAllowsCellularAccess = call.arguments["iosAllowsCellularAccess"];
bool? iosAllowsConstrainedNetworkAccess = call.arguments["iosAllowsConstrainedNetworkAccess"];
bool? iosAllowsExpensiveNetworkAccess = call.arguments["iosAllowsExpensiveNetworkAccess"];
int? iosCachePolicy = call.arguments["iosCachePolicy"];
bool? iosHttpShouldHandleCookies = call.arguments["iosHttpShouldHandleCookies"];
bool? iosHttpShouldUsePipelining = call.arguments["iosHttpShouldUsePipelining"];
int? iosNetworkServiceType = call.arguments["iosNetworkServiceType"];
double? iosTimeoutInterval = call.arguments["iosTimeoutInterval"];
ShouldOverrideUrlLoadingRequest shouldOverrideUrlLoadingRequest =
ShouldOverrideUrlLoadingRequest(
@ -156,7 +164,15 @@ class InAppWebViewController {
androidHasGesture: androidHasGesture,
androidIsRedirect: androidIsRedirect,
iosWKNavigationType:
IOSWKNavigationType.fromValue(iosWKNavigationType));
IOSWKNavigationType.fromValue(iosWKNavigationType),
iosAllowsCellularAccess: iosAllowsCellularAccess,
iosAllowsConstrainedNetworkAccess: iosAllowsConstrainedNetworkAccess,
iosAllowsExpensiveNetworkAccess: iosAllowsExpensiveNetworkAccess,
iosCachePolicy: IOSURLRequestCachePolicy.fromValue(iosCachePolicy),
iosHttpShouldHandleCookies: iosHttpShouldHandleCookies,
iosHttpShouldUsePipelining: iosHttpShouldUsePipelining,
iosNetworkServiceType: IOSURLRequestNetworkServiceType.fromValue(iosNetworkServiceType),
iosTimeoutInterval: iosTimeoutInterval);
if (_webview != null && _webview!.shouldOverrideUrlLoading != null)
return (await _webview!.shouldOverrideUrlLoading!(
@ -221,6 +237,14 @@ class InAppWebViewController {
bool? androidIsUserGesture = call.arguments["androidIsUserGesture"];
int? iosWKNavigationType = call.arguments["iosWKNavigationType"];
bool? iosIsForMainFrame = call.arguments["iosIsForMainFrame"];
bool? iosAllowsCellularAccess = call.arguments["iosAllowsCellularAccess"];
bool? iosAllowsConstrainedNetworkAccess = call.arguments["iosAllowsConstrainedNetworkAccess"];
bool? iosAllowsExpensiveNetworkAccess = call.arguments["iosAllowsExpensiveNetworkAccess"];
int? iosCachePolicy = call.arguments["iosCachePolicy"];
bool? iosHttpShouldHandleCookies = call.arguments["iosHttpShouldHandleCookies"];
bool? iosHttpShouldUsePipelining = call.arguments["iosHttpShouldUsePipelining"];
int? iosNetworkServiceType = call.arguments["iosNetworkServiceType"];
double? iosTimeoutInterval = call.arguments["iosTimeoutInterval"];
CreateWindowRequest createWindowRequest = CreateWindowRequest(
url: url,
@ -229,7 +253,15 @@ class InAppWebViewController {
androidIsUserGesture: androidIsUserGesture,
iosWKNavigationType:
IOSWKNavigationType.fromValue(iosWKNavigationType),
iosIsForMainFrame: iosIsForMainFrame);
iosIsForMainFrame: iosIsForMainFrame,
iosAllowsCellularAccess: iosAllowsCellularAccess,
iosAllowsConstrainedNetworkAccess: iosAllowsConstrainedNetworkAccess,
iosAllowsExpensiveNetworkAccess: iosAllowsExpensiveNetworkAccess,
iosCachePolicy: IOSURLRequestCachePolicy.fromValue(iosCachePolicy),
iosHttpShouldHandleCookies: iosHttpShouldHandleCookies,
iosHttpShouldUsePipelining: iosHttpShouldUsePipelining,
iosNetworkServiceType: IOSURLRequestNetworkServiceType.fromValue(iosNetworkServiceType),
iosTimeoutInterval: iosTimeoutInterval);
bool? result = false;
@ -614,6 +646,35 @@ class InAppWebViewController {
else if (_inAppBrowser != null)
_inAppBrowser!.iosOnDidReceiveServerRedirectForProvisionalNavigation();
break;
case "onNavigationResponse":
String? url = call.arguments["url"];
bool isForMainFrame = call.arguments["isForMainFrame"];
bool canShowMIMEType = call.arguments["canShowMIMEType"];
int expectedContentLength = call.arguments["expectedContentLength"];
String? mimeType = call.arguments["mimeType"];
String? suggestedFilename = call.arguments["suggestedFilename"];
String? textEncodingName = call.arguments["textEncodingName"];
IOSNavigationResponse iosOnNavigationResponse =
IOSNavigationResponse(
url: url,
isForMainFrame: isForMainFrame,
canShowMIMEType: canShowMIMEType,
expectedContentLength: expectedContentLength,
mimeType: mimeType,
suggestedFilename: suggestedFilename,
textEncodingName: textEncodingName
);
if (_webview != null && _webview!.iosOnNavigationResponse != null)
return (await _webview!.iosOnNavigationResponse!(
this, iosOnNavigationResponse))
?.toMap();
else if (_inAppBrowser != null)
return (await _inAppBrowser!
.iosOnNavigationResponse(iosOnNavigationResponse))
?.toMap();
break;
case "onLongPressHitTestResult":
Map<dynamic, dynamic>? hitTestResultMap =
call.arguments["hitTestResult"];

View File

@ -3157,6 +3157,181 @@ class IOSWKNavigationType {
int get hashCode => _value.hashCode;
}
///An iOS-specific Class that represents the constants used to specify interaction with the cached responses.
class IOSURLRequestCachePolicy {
final int _value;
const IOSURLRequestCachePolicy._internal(this._value);
static final Set<IOSURLRequestCachePolicy> values = [
IOSURLRequestCachePolicy.USE_PROTOCOL_CACHE_POLICY,
IOSURLRequestCachePolicy.RELOAD_IGNORING_LOCAL_CACHE_DATA,
IOSURLRequestCachePolicy.RELOAD_IGNORING_LOCAL_AND_REMOTE_CACHE_DATA,
IOSURLRequestCachePolicy.RETURN_CACHE_DATA_ELSE_LOAD,
IOSURLRequestCachePolicy.RETURN_CACHE_DATA_DONT_LOAD,
IOSURLRequestCachePolicy.RELOAD_REVALIDATING_CACHE_DATA,
].toSet();
static IOSURLRequestCachePolicy? fromValue(int? value) {
if (value != null) {
try {
return IOSURLRequestCachePolicy.values.firstWhere(
(element) => element.toValue() == value);
} catch (e) {
return null;
}
}
return null;
}
int toValue() => _value;
@override
String toString() {
switch (_value) {
case 1:
return "RELOAD_IGNORING_LOCAL_CACHE_DATA";
case 2:
return "RETURN_CACHE_DATA_ELSE_LOAD";
case 3:
return "RETURN_CACHE_DATA_DONT_LOAD";
case 4:
return "RELOAD_IGNORING_LOCAL_AND_REMOTE_CACHE_DATA";
case 5:
return "RELOAD_REVALIDATING_CACHE_DATA";
case 0:
default:
return "USE_PROTOCOL_CACHE_POLICY";
}
}
///Use the caching logic defined in the protocol implementation, if any, for a particular URL load request.
///This is the default policy for URL load requests.
static const USE_PROTOCOL_CACHE_POLICY = const IOSURLRequestCachePolicy._internal(0);
///The URL load should be loaded only from the originating source.
///This policy specifies that no existing cache data should be used to satisfy a URL load request.
///
///**NOTE**: Always use this policy if you are making HTTP or HTTPS byte-range requests.
static const RELOAD_IGNORING_LOCAL_CACHE_DATA = const IOSURLRequestCachePolicy._internal(1);
///Use existing cache data, regardless or age or expiration date, loading from originating source only if there is no cached data.
static const RETURN_CACHE_DATA_ELSE_LOAD = const IOSURLRequestCachePolicy._internal(2);
///Use existing cache data, regardless or age or expiration date, and fail if no cached data is available.
///
///If there is no existing data in the cache corresponding to a URL load request,
///no attempt is made to load the data from the originating source, and the load is considered to have failed.
///This constant specifies a behavior that is similar to an offline mode.
static const RETURN_CACHE_DATA_DONT_LOAD = const IOSURLRequestCachePolicy._internal(3);
///Ignore local cache data, and instruct proxies and other intermediates to disregard their caches so far as the protocol allows.
///
///**NOTE**: Versions earlier than macOS 15, iOS 13, watchOS 6, and tvOS 13 dont implement this constant.
static const RELOAD_IGNORING_LOCAL_AND_REMOTE_CACHE_DATA = const IOSURLRequestCachePolicy._internal(4);
///Use cache data if the origin source can validate it; otherwise, load from the origin.
///
///**NOTE**: Versions earlier than macOS 15, iOS 13, watchOS 6, and tvOS 13 dont implement this constant.
static const RELOAD_REVALIDATING_CACHE_DATA = const IOSURLRequestCachePolicy._internal(5);
bool operator ==(value) => value == _value;
@override
int get hashCode => _value.hashCode;
}
///An iOS-specific Class that represents the constants that specify how a request uses network resources.
class IOSURLRequestNetworkServiceType {
final int _value;
const IOSURLRequestNetworkServiceType._internal(this._value);
static final Set<IOSURLRequestNetworkServiceType> values = [
IOSURLRequestNetworkServiceType.DEFAULT,
IOSURLRequestNetworkServiceType.VIDEO,
IOSURLRequestNetworkServiceType.BACKGROUND,
IOSURLRequestNetworkServiceType.VOICE,
IOSURLRequestNetworkServiceType.RESPONSIVE_DATA,
IOSURLRequestNetworkServiceType.AV_STREAMING,
IOSURLRequestNetworkServiceType.RESPONSIVE_AV,
IOSURLRequestNetworkServiceType.CALL_SIGNALING,
].toSet();
static IOSURLRequestNetworkServiceType? fromValue(int? value) {
if (value != null) {
try {
return IOSURLRequestNetworkServiceType.values.firstWhere(
(element) => element.toValue() == value);
} catch (e) {
return null;
}
}
return null;
}
int toValue() => _value;
@override
String toString() {
switch (_value) {
case 2:
return "VIDEO";
case 3:
return "BACKGROUND";
case 4:
return "VOICE";
case 6:
return "RESPONSIVE_DATA";
case 8:
return "AV_STREAMING";
case 9:
return "RESPONSIVE_AV";
case 11:
return "CALL_SIGNALING";
case 0:
default:
return "DEFAULT";
}
}
///A service type for standard network traffic.
static const DEFAULT = const IOSURLRequestNetworkServiceType._internal(0);
///A service type for video traffic.
static const VIDEO = const IOSURLRequestNetworkServiceType._internal(2);
///A service type for background traffic.
///
///You should specify this type if your app is performing a download that was not requested by the userfor example,
///prefetching content so that it will be available when the user chooses to view it.
static const BACKGROUND = const IOSURLRequestNetworkServiceType._internal(3);
///A service type for voice traffic.
static const VOICE = const IOSURLRequestNetworkServiceType._internal(4);
///A service type for data that the user is actively waiting for.
///
///Use this service type for interactive situations where the user is anticipating a quick response, like instant messaging or completing a purchase.
static const RESPONSIVE_DATA = const IOSURLRequestNetworkServiceType._internal(6);
///A service type for streaming audio/video data.
static const AV_STREAMING = const IOSURLRequestNetworkServiceType._internal(8);
///A service type for responsive (time-sensitive) audio/video data.
static const RESPONSIVE_AV = const IOSURLRequestNetworkServiceType._internal(9);
///A service type for call signaling.
///
///Use this service type with network traffic that establishes, maintains, or tears down a VoIP call.
static const CALL_SIGNALING = const IOSURLRequestNetworkServiceType._internal(11);
bool operator ==(value) => value == _value;
@override
int get hashCode => _value.hashCode;
}
///Class that represents the navigation request used by the [WebView.shouldOverrideUrlLoading] event.
class ShouldOverrideUrlLoadingRequest {
///Represents the url of the navigation request.
@ -3189,6 +3364,46 @@ class ShouldOverrideUrlLoadingRequest {
///**NOTE**: available only on iOS.
IOSWKNavigationType? iosWKNavigationType;
///A Boolean value indicating whether the request is allowed to use the built-in cellular radios to satisfy the request.
///
///**NOTE**: available only on iOS.
bool? iosAllowsCellularAccess;
///A Boolean value that indicates whether the request may use the network when the user has specified Low Data Mode.
///
///**NOTE**: available only on iOS 13.0+.
bool? iosAllowsConstrainedNetworkAccess;
///A Boolean value that indicates whether connections may use a network interface that the system considers expensive.
///
///**NOTE**: available only on iOS 13.0+.
bool? iosAllowsExpensiveNetworkAccess;
///The requests cache policy.
///
///**NOTE**: available only on iOS.
IOSURLRequestCachePolicy? iosCachePolicy;
///A Boolean value indicating whether cookies will be sent with and set for this request.
///
///**NOTE**: available only on iOS.
bool? iosHttpShouldHandleCookies;
///A Boolean value indicating whether the request should transmit before the previous response is received.
///
///**NOTE**: available only on iOS.
bool? iosHttpShouldUsePipelining;
///The service type associated with this request.
///
///**NOTE**: available only on iOS.
IOSURLRequestNetworkServiceType? iosNetworkServiceType;
///The timeout interval of the request.
///
///**NOTE**: available only on iOS.
double? iosTimeoutInterval;
ShouldOverrideUrlLoadingRequest(
{required this.url,
this.method,
@ -3196,7 +3411,15 @@ class ShouldOverrideUrlLoadingRequest {
required this.isForMainFrame,
this.androidHasGesture,
this.androidIsRedirect,
this.iosWKNavigationType});
this.iosWKNavigationType,
this.iosAllowsCellularAccess,
this.iosAllowsConstrainedNetworkAccess,
this.iosAllowsExpensiveNetworkAccess,
this.iosCachePolicy,
this.iosHttpShouldHandleCookies,
this.iosHttpShouldUsePipelining,
this.iosNetworkServiceType,
this.iosTimeoutInterval});
Map<String, dynamic> toMap() {
return {
@ -3206,7 +3429,15 @@ class ShouldOverrideUrlLoadingRequest {
"isForMainFrame": isForMainFrame,
"androidHasGesture": androidHasGesture,
"androidIsRedirect": androidIsRedirect,
"iosWKNavigationType": iosWKNavigationType?.toValue()
"iosWKNavigationType": iosWKNavigationType?.toValue(),
"iosAllowsCellularAccess": iosAllowsCellularAccess,
"iosAllowsConstrainedNetworkAccess": iosAllowsConstrainedNetworkAccess,
"iosAllowsExpensiveNetworkAccess": iosAllowsExpensiveNetworkAccess,
"iosCachePolicy": iosCachePolicy?.toValue(),
"iosHttpShouldHandleCookies": iosHttpShouldHandleCookies,
"iosHttpShouldUsePipelining": iosHttpShouldUsePipelining,
"iosNetworkServiceType": iosNetworkServiceType?.toValue(),
"iosTimeoutInterval": iosTimeoutInterval,
};
}
@ -3250,21 +3481,78 @@ class CreateWindowRequest {
///**NOTE**: available only on iOS.
bool? iosIsForMainFrame;
///A Boolean value indicating whether the request is allowed to use the built-in cellular radios to satisfy the request.
///
///**NOTE**: available only on iOS.
bool? iosAllowsCellularAccess;
///A Boolean value that indicates whether the request may use the network when the user has specified Low Data Mode.
///
///**NOTE**: available only on iOS 13.0+.
bool? iosAllowsConstrainedNetworkAccess;
///A Boolean value that indicates whether connections may use a network interface that the system considers expensive.
///
///**NOTE**: available only on iOS 13.0+.
bool? iosAllowsExpensiveNetworkAccess;
///The requests cache policy.
///
///**NOTE**: available only on iOS.
IOSURLRequestCachePolicy? iosCachePolicy;
///A Boolean value indicating whether cookies will be sent with and set for this request.
///
///**NOTE**: available only on iOS.
bool? iosHttpShouldHandleCookies;
///A Boolean value indicating whether the request should transmit before the previous response is received.
///
///**NOTE**: available only on iOS.
bool? iosHttpShouldUsePipelining;
///The service type associated with this request.
///
///**NOTE**: available only on iOS.
IOSURLRequestNetworkServiceType? iosNetworkServiceType;
///The timeout interval of the request.
///
///**NOTE**: available only on iOS.
double? iosTimeoutInterval;
CreateWindowRequest(
{this.url,
required this.windowId,
this.androidIsDialog,
this.androidIsUserGesture,
this.iosWKNavigationType,
this.iosIsForMainFrame});
this.iosIsForMainFrame,
this.iosAllowsCellularAccess,
this.iosAllowsConstrainedNetworkAccess,
this.iosAllowsExpensiveNetworkAccess,
this.iosCachePolicy,
this.iosHttpShouldHandleCookies,
this.iosHttpShouldUsePipelining,
this.iosNetworkServiceType,
this.iosTimeoutInterval});
Map<String, dynamic> toMap() {
return {
"url": url,
"windowId": windowId,
"androidIsDialog": androidIsDialog,
"androidIsUserGesture": androidIsUserGesture,
"iosWKNavigationType": iosWKNavigationType?.toValue(),
"url": url,
"windowId": windowId
"iosIsForMainFrame": iosIsForMainFrame,
"iosAllowsCellularAccess": iosAllowsCellularAccess,
"iosAllowsConstrainedNetworkAccess": iosAllowsConstrainedNetworkAccess,
"iosAllowsExpensiveNetworkAccess": iosAllowsExpensiveNetworkAccess,
"iosCachePolicy": iosCachePolicy?.toValue(),
"iosHttpShouldHandleCookies": iosHttpShouldHandleCookies,
"iosHttpShouldUsePipelining": iosHttpShouldUsePipelining,
"iosNetworkServiceType": iosNetworkServiceType?.toValue(),
"iosTimeoutInterval": iosTimeoutInterval,
};
}
@ -5161,4 +5449,86 @@ class CSSLinkHtmlTagAttributes {
String toString() {
return toMap().toString();
}
}
}
///An iOS-specific Class that represents the navigation response used by the [WebView.iosOnNavigationResponse] event.
class IOSNavigationResponse {
///The URL for the response.
String? url;
///A Boolean value that indicates whether the response targets the web views main frame.
bool isForMainFrame;
///A Boolean value that indicates whether WebKit is capable of displaying the responses MIME type natively.
bool canShowMIMEType;
///The expected length of the responses content.
int expectedContentLength;
///The MIME type of the response.
String? mimeType;
///A suggested filename for the response data.
String? suggestedFilename;
///The name of the text encoding provided by the responses originating source.
String? textEncodingName;
IOSNavigationResponse({this.url,
required this.isForMainFrame,
required this.canShowMIMEType,
required this.expectedContentLength,
this.mimeType,
this.suggestedFilename,
this.textEncodingName
});
Map<String, dynamic> toMap() {
return {
"url": this.url,
"isForMainFrame": this.isForMainFrame,
"canShowMIMEType": this.canShowMIMEType,
"expectedContentLength": this.expectedContentLength,
"mimeType": this.mimeType,
"suggestedFilename": this.suggestedFilename,
"textEncodingName": this.textEncodingName,
};
}
Map<String, dynamic> toJson() {
return this.toMap();
}
@override
String toString() {
return toMap().toString();
}
}
///Class that is used by [WebView.iosOnNavigationResponse] event.
///It represents the policy to pass back to the decision handler.
class IOSNavigationResponseAction {
final int _value;
const IOSNavigationResponseAction._internal(this._value);
int toValue() => _value;
///Cancel the navigation.
static const CANCEL = const IOSNavigationResponseAction._internal(0);
///Allow the navigation to continue.
static const ALLOW = const IOSNavigationResponseAction._internal(1);
bool operator ==(value) => value == _value;
@override
int get hashCode => _value.hashCode;
Map<String, dynamic> toMap() {
return {
"action": _value,
};
}
}

View File

@ -604,6 +604,17 @@ abstract class WebView {
final void Function(InAppWebViewController controller)?
iosOnDidReceiveServerRedirectForProvisionalNavigation;
///Called when a web view asks for permission to navigate to new content after the response to the navigation request is known.
///
///[navigationResponse] represents the navigation response.
///
///**NOTE**: available only on iOS.
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview
final Future<IOSNavigationResponseAction?> Function(InAppWebViewController controller,
IOSNavigationResponse navigationResponse)?
iosOnNavigationResponse;
///Initial url that will be loaded.
final String? initialUrl;
@ -680,6 +691,7 @@ abstract class WebView {
this.androidOnReceivedLoginRequest,
this.iosOnWebContentProcessDidTerminate,
this.iosOnDidReceiveServerRedirectForProvisionalNavigation,
this.iosOnNavigationResponse,
this.initialUrl,
this.initialFile,
this.initialData,

View File

@ -80,13 +80,13 @@ class ChromeSafariBrowserOptions {
///This class represents all the cross-platform WebView options available.
class InAppWebViewOptions
implements WebViewOptions, BrowserOptions, AndroidOptions, IosOptions {
///Set to `true` to be able to listen at the [shouldOverrideUrlLoading] event. The default value is `false`.
///Set to `true` to be able to listen at the [WebView.shouldOverrideUrlLoading] event. The default value is `false`.
bool useShouldOverrideUrlLoading;
///Set to `true` to be able to listen at the [onLoadResource] event. The default value is `false`.
///Set to `true` to be able to listen at the [WebView.onLoadResource] event. The default value is `false`.
bool useOnLoadResource;
///Set to `true` to be able to listen at the [onDownloadStart] event. The default value is `false`.
///Set to `true` to be able to listen at the [WebView.onDownloadStart] event. The default value is `false`.
bool useOnDownloadStart;
///Set to `true` to have all the browser's cache cleared before the new WebView is opened. The default value is `false`.
@ -122,7 +122,7 @@ class InAppWebViewOptions
///Define whether the horizontal scrollbar should be drawn or not. The default value is `true`.
bool horizontalScrollBarEnabled;
///List of custom schemes that the WebView must handle. Use the [onLoadResourceCustomScheme] event to intercept resource requests with custom scheme.
///List of custom schemes that the WebView must handle. Use the [WebView.onLoadResourceCustomScheme] event to intercept resource requests with custom scheme.
///
///**NOTE**: available on iOS 11.0+.
List<String> resourceCustomSchemes;
@ -137,10 +137,10 @@ class InAppWebViewOptions
///**NOTE**: available on iOS 13.0+.
UserPreferredContentMode? preferredContentMode;
///Set to `true` to be able to listen at the [shouldInterceptAjaxRequest] event. The default value is `false`.
///Set to `true` to be able to listen at the [WebView.shouldInterceptAjaxRequest] event. The default value is `false`.
bool useShouldInterceptAjaxRequest;
///Set to `true` to be able to listen at the [shouldInterceptFetchRequest] event. The default value is `false`.
///Set to `true` to be able to listen at the [WebView.shouldInterceptFetchRequest] event. The default value is `false`.
bool useShouldInterceptFetchRequest;
///Set to `true` to open a browser window with incognito mode. The default value is `false`.
@ -474,10 +474,10 @@ class AndroidInAppWebViewOptions
bool hardwareAcceleration;
///Sets whether the WebView supports multiple windows.
///If set to `true`, [onCreateWindow] event must be implemented by the host application. The default value is `false`.
///If set to `true`, [WebView.onCreateWindow] event must be implemented by the host application. The default value is `false`.
bool supportMultipleWindows;
///Regular expression used by [shouldOverrideUrlLoading] event to cancel navigation requests for frames that are not the main frame.
///Regular expression used by [WebView.shouldOverrideUrlLoading] event to cancel navigation requests for frames that are not the main frame.
///If the url request of a subframe matches the regular expression, then the request of that subframe is canceled.
String? regexToCancelSubFramesLoading;
@ -866,6 +866,9 @@ class IOSInAppWebViewOptions
///**NOTE**: available on iOS 14.0+.
bool limitsNavigationsToAppBoundDomains;
///Set to `true` to be able to listen at the [WebView.iosOnNavigationResponse] event. The default value is `false`.
bool useOnNavigationResponse;
IOSInAppWebViewOptions(
{this.disallowOverScroll = false,
this.enableViewportScale = false,
@ -894,7 +897,8 @@ class IOSInAppWebViewOptions
this.isDirectionalLockEnabled = false,
this.mediaType,
this.pageZoom = 1.0,
this.limitsNavigationsToAppBoundDomains = false});
this.limitsNavigationsToAppBoundDomains = false,
this.useOnNavigationResponse = false});
@override
Map<String, dynamic> toMap() {
@ -934,6 +938,7 @@ class IOSInAppWebViewOptions
"mediaType": mediaType,
"pageZoom": pageZoom,
"limitsNavigationsToAppBoundDomains": limitsNavigationsToAppBoundDomains,
"useOnNavigationResponse": useOnNavigationResponse,
};
}
@ -987,6 +992,7 @@ class IOSInAppWebViewOptions
options.mediaType = map["mediaType"];
options.pageZoom = map["pageZoom"];
options.limitsNavigationsToAppBoundDomains = map["limitsNavigationsToAppBoundDomains"];
options.useOnNavigationResponse = map["useOnNavigationResponse"];
return options;
}