From fcb965eac67485798008a45a4f0ef66d1648aff8 Mon Sep 17 00:00:00 2001 From: xuty Date: Fri, 27 Mar 2020 07:40:52 +0800 Subject: [PATCH 01/11] fix cookie .secure property --- ios/Classes/MyCookieManager.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ios/Classes/MyCookieManager.swift b/ios/Classes/MyCookieManager.swift index 5c44171f..289342a7 100755 --- a/ios/Classes/MyCookieManager.swift +++ b/ios/Classes/MyCookieManager.swift @@ -90,7 +90,9 @@ class MyCookieManager: NSObject, FlutterPlugin { if maxAge != nil { properties[.maximumAge] = String(maxAge!) } - properties[.secure] = (isSecure != nil && isSecure!) ? "TRUE" : "FALSE" + if isSecure != nil && isSecure! { + properties[.secure] = "TRUE" + } let cookie = HTTPCookie(properties: properties)! MyCookieManager.httpCookieStore!.setCookie(cookie, completionHandler: {() in From 6e18699dd517ad1d2b36840f64282f3bb8852553 Mon Sep 17 00:00:00 2001 From: savy91 Date: Mon, 19 Apr 2021 17:51:24 +0200 Subject: [PATCH 02/11] Make sure that when we want to open a new instance of a custom chrome tab, we are opening a new instance with the provided url and not an old instance. --- android/src/main/AndroidManifest.xml | 4 +++- .../ChromeSafariBrowserManager.java | 3 +++ .../android/chrome_custom_tabs_options.dart | 11 +++++++++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 40dc1267..daff949a 100755 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -8,7 +8,9 @@ android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density" /> + android:name="com.pichillilorenzo.flutter_inappwebview.chrome_custom_tabs.ChromeCustomTabsActivity" + android:launchMode="singleInstance" + /> toMap() { @@ -53,7 +58,8 @@ class AndroidChromeCustomTabsOptions "enableUrlBarHiding": enableUrlBarHiding, "instantAppsEnabled": instantAppsEnabled, "packageName": packageName, - "keepAliveEnabled": keepAliveEnabled + "keepAliveEnabled": keepAliveEnabled, + "noHistory": noHistory }; } @@ -68,6 +74,7 @@ class AndroidChromeCustomTabsOptions options.instantAppsEnabled = map["instantAppsEnabled"]; options.packageName = map["packageName"]; options.keepAliveEnabled = map["keepAliveEnabled"]; + options.noHistory = map["noHistory"]; return options; } From 19ffe781e68dcc9fdd339bf0508642407a47ce11 Mon Sep 17 00:00:00 2001 From: Caleb Jones Date: Mon, 3 May 2021 10:31:54 -0400 Subject: [PATCH 03/11] Fix parsing crash on null value. --- ios/Classes/Options.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ios/Classes/Options.swift b/ios/Classes/Options.swift index 286a2f83..96ba50eb 100755 --- a/ios/Classes/Options.swift +++ b/ios/Classes/Options.swift @@ -16,8 +16,10 @@ public class Options: NSObject { func parse(options: [String: Any?]) -> Options { for (key, value) in options { - if value != nil, !(value is NSNull), self.responds(to: Selector(key)) { - self.setValue(value, forKey: key) + if !(value is NSNull) { + if self.responds(to: Selector(key)) { + self.setValue(value, forKey: key) + } } } return self From 397998788b258ca1a9d0407d2dd1bde683b4e50a Mon Sep 17 00:00:00 2001 From: aleksandrpaskevic Date: Mon, 31 May 2021 17:18:06 +0300 Subject: [PATCH 04/11] fix bug when in String[] array come null --- .../in_app_webview/InAppWebViewChromeClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebViewChromeClient.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebViewChromeClient.java index 224d75cd..c4afd089 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebViewChromeClient.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebViewChromeClient.java @@ -1004,7 +1004,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR private Boolean arrayContainsString(String[] array, String pattern) { for (String content : array) { - if (content.contains(pattern)) { + if (content != null && content.contains(pattern)) { return true; } } From bedceb0a14d499412eb616f54fe95efd1ad5b7e8 Mon Sep 17 00:00:00 2001 From: Christoph Eck Date: Mon, 14 Jun 2021 16:56:26 +0200 Subject: [PATCH 05/11] fix: use in NavigationAction request toMap method --- lib/src/types.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/types.dart b/lib/src/types.dart index eb9d1d68..e97f9ff8 100755 --- a/lib/src/types.dart +++ b/lib/src/types.dart @@ -4052,7 +4052,7 @@ class NavigationAction { Map toMap() { return { - "request": request.toString(), + "request": request.toMap(), "isForMainFrame": isForMainFrame, "androidHasGesture": androidHasGesture, "androidIsRedirect": androidIsRedirect, From d8d84053bc1727269a4f8be9b4b38485dc111c0d Mon Sep 17 00:00:00 2001 From: sunalwaysknows <86180691+sunalwaysknows@users.noreply.github.com> Date: Sat, 26 Feb 2022 11:06:25 +0800 Subject: [PATCH 06/11] Update: Append optional applicationNameForUserAgent to configrational applicationNameForUserAgent --- ios/Classes/InAppWebView/InAppWebView.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ios/Classes/InAppWebView/InAppWebView.swift b/ios/Classes/InAppWebView/InAppWebView.swift index f49fcef7..8ad01295 100755 --- a/ios/Classes/InAppWebView/InAppWebView.swift +++ b/ios/Classes/InAppWebView/InAppWebView.swift @@ -499,6 +499,11 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi } else if options.cacheEnabled { configuration.websiteDataStore = WKWebsiteDataStore.default() } + if !options.applicationNameForUserAgent.isEmpty { + if let applicationNameForUserAgent = configuration.applicationNameForUserAgent { + configuration.applicationNameForUserAgent = applicationNameForUserAgent + " " + options.applicationNameForUserAgent + } + } } if #available(iOS 10.0, *) { From f074b7a011ceae60b9d602c4e58b0e20bc75d605 Mon Sep 17 00:00:00 2001 From: Lorenzo Pichilli Date: Fri, 15 Apr 2022 19:20:35 +0200 Subject: [PATCH 07/11] created android InAppWebViewInterface to implement other android webview engines, getOriginalUrl method is cross-platform now, Fixed requestImageRef method always null on iOS --- .idea/libraries/Dart_Packages.xml | 420 ------------------ .idea/libraries/Dart_SDK.xml | 40 +- .idea/libraries/Flutter_Plugins.xml | 4 +- .idea/misc.xml | 3 + CHANGELOG.md | 5 + .../FlutterWebViewFactory.java | 18 +- .../InAppWebViewFlutterPlugin.java | 1 - .../InAppWebViewMethodHandler.java | 122 +++-- .../InAppWebViewStatic.java | 6 +- .../flutter_inappwebview/Util.java | 32 +- .../in_app_webview/FlutterWebView.java | 4 +- .../in_app_webview/InAppWebView.java | 163 +++++-- .../in_app_webview/InAppWebViewOptions.java | 8 +- .../plugin_scripts_js/JavaScriptBridgeJS.java | 5 +- .../types/HitTestResult.java | 79 ++++ .../types/InAppWebViewInterface.java | 101 +++++ .../types/PlatformWebView.java | 9 + .../types/UserContentController.java | 8 +- .../types/WebMessage.java | 23 + .../types/WebMessageChannel.java | 171 ++++--- .../types/WebMessageListener.java | 185 +++++++- .../types/WebMessagePort.java | 130 ++++++ .../types/WebViewImplementation.java | 32 ++ example/.flutter-plugins-dependencies | 2 +- example/ios/Flutter/AppFrameworkInfo.plist | 2 +- example/ios/Flutter/Flutter.podspec | 2 +- .../ios/Flutter/flutter_export_environment.sh | 8 +- example/ios/Runner.xcodeproj/project.pbxproj | 4 +- flutter_inappwebview.iml | 1 - ios/Classes/InAppWebView/InAppWebView.swift | 14 +- ios/Classes/InAppWebViewMethodHandler.swift | 5 +- .../LastTouchedAnchorOrImageJS.swift | 4 +- .../WebMessageListenerJS.swift | 6 +- ios/Classes/Types/WebMessageListener.swift | 3 +- lib/{ => assets}/t_rex_runner/t-rex.css | 0 lib/{ => assets}/t_rex_runner/t-rex.html | 0 lib/src/in_app_browser/in_app_browser.dart | 35 +- .../android/in_app_webview_controller.dart | 10 +- .../headless_in_app_webview.dart | 5 + lib/src/in_app_webview/in_app_webview.dart | 9 +- .../in_app_webview_controller.dart | 355 ++++++++++----- lib/src/in_app_webview/webview.dart | 35 +- lib/src/types.dart | 48 ++ pubspec.yaml | 6 +- 44 files changed, 1320 insertions(+), 803 deletions(-) delete mode 100644 .idea/libraries/Dart_Packages.xml rename android/src/main/java/com/pichillilorenzo/flutter_inappwebview/{in_app_webview => }/FlutterWebViewFactory.java (54%) create mode 100644 android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/HitTestResult.java create mode 100644 android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/InAppWebViewInterface.java create mode 100644 android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/PlatformWebView.java create mode 100644 android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/WebMessage.java create mode 100644 android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/WebMessagePort.java create mode 100644 android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/WebViewImplementation.java rename lib/{ => assets}/t_rex_runner/t-rex.css (100%) rename lib/{ => assets}/t_rex_runner/t-rex.html (100%) diff --git a/.idea/libraries/Dart_Packages.xml b/.idea/libraries/Dart_Packages.xml deleted file mode 100644 index 3b0b4626..00000000 --- a/.idea/libraries/Dart_Packages.xml +++ /dev/null @@ -1,420 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Dart_SDK.xml b/.idea/libraries/Dart_SDK.xml index 62be7eab..1889af48 100755 --- a/.idea/libraries/Dart_SDK.xml +++ b/.idea/libraries/Dart_SDK.xml @@ -1,26 +1,26 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + diff --git a/.idea/libraries/Flutter_Plugins.xml b/.idea/libraries/Flutter_Plugins.xml index 31799730..65bb3679 100755 --- a/.idea/libraries/Flutter_Plugins.xml +++ b/.idea/libraries/Flutter_Plugins.xml @@ -1,6 +1,8 @@ - + + + diff --git a/.idea/misc.xml b/.idea/misc.xml index eac9d8a9..5d05d05d 100755 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,5 +1,8 @@ + + + \ No newline at end of file diff --git a/ios/Classes/InAppWebView/InAppWebView.swift b/ios/Classes/InAppWebView/InAppWebView.swift index f49fcef7..07b6d293 100755 --- a/ios/Classes/InAppWebView/InAppWebView.swift +++ b/ios/Classes/InAppWebView/InAppWebView.swift @@ -19,6 +19,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi var pullToRefreshControl: PullToRefreshControl? var webMessageChannels: [String:WebMessageChannel] = [:] var webMessageListeners: [WebMessageListener] = [] + var currentOriginalUrl: URL? static var sslCertificatesMap: [String: SslCertificate] = [:] // [URL host name : SslCertificate] static var credentialsProposed: [URLCredential] = [] @@ -26,6 +27,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi var lastScrollX: CGFloat = 0 var lastScrollY: CGFloat = 0 + // Used to manage pauseTimers() and resumeTimers() var isPausedTimers = false var isPausedTimersCompletionHandler: (() -> Void)? @@ -1557,6 +1559,9 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi } public func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) { + currentOriginalUrl = url + lastTouchPoint = nil + disposeWebMessageChannels() initializeWindowIdJS() @@ -2723,6 +2728,10 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) { scrollView.setZoomScale(currentZoomScale * CGFloat(zoomFactor), animated: animated) } + public func getOriginalUrl() -> URL? { + return currentOriginalUrl + } + public func getZoomScale() -> Float { return Float(scrollView.zoomScale) } @@ -2883,10 +2892,7 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) { } public func dispose() { - if isPausedTimers, let completionHandler = isPausedTimersCompletionHandler { - isPausedTimersCompletionHandler = nil - completionHandler() - } + resumeTimers() stopLoading() disposeWebMessageChannels() for webMessageListener in webMessageListeners { diff --git a/ios/Classes/InAppWebViewMethodHandler.swift b/ios/Classes/InAppWebViewMethodHandler.swift index 58092f63..7216bc09 100644 --- a/ios/Classes/InAppWebViewMethodHandler.swift +++ b/ios/Classes/InAppWebViewMethodHandler.swift @@ -290,7 +290,7 @@ public class InAppWebViewMethodHandler: FlutterMethodCallDelegate { break case "zoomBy": let zoomFactor = arguments!["zoomFactor"] as! Float - let animated = arguments!["iosAnimated"] as! Bool + let animated = arguments!["animated"] as! Bool webView?.zoomBy(zoomFactor: zoomFactor, animated: animated) result(true) break @@ -298,6 +298,9 @@ public class InAppWebViewMethodHandler: FlutterMethodCallDelegate { webView?.reloadFromOrigin() result(true) break + case "getOriginalUrl": + result(webView?.getOriginalUrl()?.absoluteString) + break case "getZoomScale": result(webView?.getZoomScale()) break diff --git a/ios/Classes/PluginScriptsJS/LastTouchedAnchorOrImageJS.swift b/ios/Classes/PluginScriptsJS/LastTouchedAnchorOrImageJS.swift index 4f3442f9..ef2e7d67 100644 --- a/ios/Classes/PluginScriptsJS/LastTouchedAnchorOrImageJS.swift +++ b/ios/Classes/PluginScriptsJS/LastTouchedAnchorOrImageJS.swift @@ -27,7 +27,7 @@ window.\(JAVASCRIPT_BRIDGE_NAME)._lastImageTouched = null; if (target.tagName === 'IMG') { var img = target; window.\(JAVASCRIPT_BRIDGE_NAME)._lastImageTouched = { - src: img.src + url: img.src }; var parent = img.parentNode; while (parent) { @@ -47,7 +47,7 @@ window.\(JAVASCRIPT_BRIDGE_NAME)._lastImageTouched = null; var images = link.getElementsByTagName('img'); var img = (images.length > 0) ? images[0] : null; var imgSrc = (img != null) ? img.src : null; - window.\(JAVASCRIPT_BRIDGE_NAME)._lastImageTouched = (img != null) ? {src: img.src} : window.\(JAVASCRIPT_BRIDGE_NAME)._lastImageTouched; + window.\(JAVASCRIPT_BRIDGE_NAME)._lastImageTouched = (img != null) ? {url: imgSrc} : window.\(JAVASCRIPT_BRIDGE_NAME)._lastImageTouched; window.\(JAVASCRIPT_BRIDGE_NAME)._lastAnchorOrImageTouched = { title: link.textContent, url: link.href, diff --git a/ios/Classes/PluginScriptsJS/WebMessageListenerJS.swift b/ios/Classes/PluginScriptsJS/WebMessageListenerJS.swift index 91ff57ea..9d9fc60d 100644 --- a/ios/Classes/PluginScriptsJS/WebMessageListenerJS.swift +++ b/ios/Classes/PluginScriptsJS/WebMessageListenerJS.swift @@ -36,7 +36,7 @@ window.\(JAVASCRIPT_BRIDGE_NAME)._normalizeIPv6 = function(ip_string) { // replace ipv4 address if any var ipv4 = ip_string.match(/(.*:)([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$)/); if (ipv4) { - var ip_string = ipv4[1]; + ip_string = ipv4[1]; ipv4 = ipv4[2].match(/[0-9]+/g); for (var i = 0;i < 4;i ++) { var byte = parseInt(ipv4[i],10); @@ -85,12 +85,12 @@ window.\(JAVASCRIPT_BRIDGE_NAME)._isOriginAllowed = function(allowedOriginRules, var IPv6 = null; if (rule.host != null && rule.host[0] === "[") { try { - IPv6 = normalizeIPv6(rule.host.substring(1, rule.host.length - 1)); + IPv6 = window.\(JAVASCRIPT_BRIDGE_NAME)._normalizeIPv6(rule.host.substring(1, rule.host.length - 1)); } catch {} } var hostIPv6 = null; try { - hostIPv6 = normalizeIPv6(host); + hostIPv6 = window.\(JAVASCRIPT_BRIDGE_NAME)._normalizeIPv6(host); } catch {} var schemeAllowed = scheme == rule.scheme; diff --git a/ios/Classes/Types/WebMessageListener.swift b/ios/Classes/Types/WebMessageListener.swift index 6e283741..0d85c331 100644 --- a/ios/Classes/Types/WebMessageListener.swift +++ b/ios/Classes/Types/WebMessageListener.swift @@ -83,8 +83,9 @@ public class WebMessageListener : FlutterMethodCallDelegate { return "'*'" } let rule = URL(string: allowedOriginRule)! + let host = rule.host != nil ? "'" + rule.host!.replacingOccurrences(of: "\'", with: "\\'") + "'" : "null" return """ - {scheme: '\(rule.scheme!)', host: '\(rule.host?.replacingOccurrences(of: "\'", with: "\\'") ?? "null")', port: \(rule.port != nil ? String(rule.port!) : "null")} + {scheme: '\(rule.scheme!)', host: \(host), port: \(rule.port != nil ? String(rule.port!) : "null")} """ }.joined(separator: ", ") let source = """ diff --git a/lib/t_rex_runner/t-rex.css b/lib/assets/t_rex_runner/t-rex.css similarity index 100% rename from lib/t_rex_runner/t-rex.css rename to lib/assets/t_rex_runner/t-rex.css diff --git a/lib/t_rex_runner/t-rex.html b/lib/assets/t_rex_runner/t-rex.html similarity index 100% rename from lib/t_rex_runner/t-rex.html rename to lib/assets/t_rex_runner/t-rex.html diff --git a/lib/src/in_app_browser/in_app_browser.dart b/lib/src/in_app_browser/in_app_browser.dart index ff4e2877..727582ae 100755 --- a/lib/src/in_app_browser/in_app_browser.dart +++ b/lib/src/in_app_browser/in_app_browser.dart @@ -64,8 +64,12 @@ class InAppBrowser { ///The window id of a [CreateWindowAction.windowId]. final int? windowId; + ///Represents the WebView native implementation to be used. + ///The default value is [WebViewImplementation.NATIVE]. + final WebViewImplementation implementation; + /// - InAppBrowser({this.windowId, this.initialUserScripts}) { + InAppBrowser({this.windowId, this.initialUserScripts, this.implementation = WebViewImplementation.NATIVE}) { id = IdGenerator.generate(); this._channel = MethodChannel('com.pichillilorenzo/flutter_inappbrowser_$id'); @@ -109,6 +113,7 @@ class InAppBrowser { () => options?.toMap() ?? InAppBrowserClassOptions().toMap()); args.putIfAbsent('contextMenu', () => contextMenu?.toMap() ?? {}); args.putIfAbsent('windowId', () => windowId); + args.putIfAbsent('implementation', () => implementation.toValue()); args.putIfAbsent('initialUserScripts', () => initialUserScripts?.map((e) => e.toMap()).toList() ?? []); args.putIfAbsent( @@ -167,6 +172,7 @@ class InAppBrowser { () => options?.toMap() ?? InAppBrowserClassOptions().toMap()); args.putIfAbsent('contextMenu', () => contextMenu?.toMap() ?? {}); args.putIfAbsent('windowId', () => windowId); + args.putIfAbsent('implementation', () => implementation.toValue()); args.putIfAbsent('initialUserScripts', () => initialUserScripts?.map((e) => e.toMap()).toList() ?? []); args.putIfAbsent( @@ -207,6 +213,7 @@ class InAppBrowser { 'historyUrl', () => androidHistoryUrl?.toString() ?? "about:blank"); args.putIfAbsent('contextMenu', () => contextMenu?.toMap() ?? {}); args.putIfAbsent('windowId', () => windowId); + args.putIfAbsent('implementation', () => implementation.toValue()); args.putIfAbsent('initialUserScripts', () => initialUserScripts?.map((e) => e.toMap()).toList() ?? []); args.putIfAbsent( @@ -290,16 +297,16 @@ class InAppBrowser { ///Event fired when the [InAppBrowser] starts to load an [url]. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onPageStarted(android.webkit.WebView,%20java.lang.String,%20android.graphics.Bitmap) - /// - ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455621-webview + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.onPageStarted](https://developer.android.com/reference/android/webkit/WebViewClient#onPageStarted(android.webkit.WebView,%20java.lang.String,%20android.graphics.Bitmap))) + ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455621-webview)) void onLoadStart(Uri? url) {} ///Event fired when the [InAppBrowser] finishes loading an [url]. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onPageFinished(android.webkit.WebView,%20java.lang.String) - /// - ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455629-webview + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.onPageFinished](https://developer.android.com/reference/android/webkit/WebViewClient#onPageFinished(android.webkit.WebView,%20java.lang.String))) + ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455629-webview)) void onLoadStop(Uri? url) {} ///Event fired when the [InAppBrowser] encounters an error loading an [url]. @@ -326,7 +333,9 @@ class InAppBrowser { ///Event fired when the current [progress] (range 0-100) of loading a page is changed. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onProgressChanged(android.webkit.WebView,%20int) + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebChromeClient.onProgressChanged](https://developer.android.com/reference/android/webkit/WebChromeClient#onProgressChanged(android.webkit.WebView,%20int))) + ///- iOS void onProgressChanged(int progress) {} ///Event fired when the [InAppBrowser] webview receives a [ConsoleMessage]. @@ -501,7 +510,7 @@ class InAppBrowser { URLAuthenticationChallenge challenge) {} ///Event fired as find-on-page operations progress. - ///The listener may be notified multiple times while the operation is underway, and the numberOfMatches value should not be considered final unless [isDoneCounting] is true. + ///The listener may be notified multiple times while the operation is underway, and the [numberOfMatches] value should not be considered final unless [isDoneCounting] is true. /// ///[activeMatchOrdinal] represents the zero-based ordinal of the currently selected match. /// @@ -509,7 +518,9 @@ class InAppBrowser { /// ///[isDoneCounting] whether the find operation has actually completed. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#setFindListener(android.webkit.WebView.FindListener) + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.FindListener.onFindResultReceived](https://developer.android.com/reference/android/webkit/WebView.FindListener#onFindResultReceived(int,%20int,%20boolean))) + ///- iOS void onFindResultReceived( int activeMatchOrdinal, int numberOfMatches, bool isDoneCounting) {} @@ -562,7 +573,9 @@ class InAppBrowser { /// ///[url] represents the url on which is called. /// - ///**NOTE**: available on Android 21+. + ///**Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS void onPrint(Uri? url) {} ///Event fired when an HTML element of the webview has been clicked and held. diff --git a/lib/src/in_app_webview/android/in_app_webview_controller.dart b/lib/src/in_app_webview/android/in_app_webview_controller.dart index 0e3c8e22..3c0805bc 100644 --- a/lib/src/in_app_webview/android/in_app_webview_controller.dart +++ b/lib/src/in_app_webview/android/in_app_webview_controller.dart @@ -59,11 +59,8 @@ class AndroidInAppWebViewController { await _channel.invokeMethod('resume', args); } - ///Gets the URL that was originally requested for the current page. - ///This is not always the same as the URL passed to [InAppWebView.onLoadStarted] because although the load for that URL has begun, - ///the current page may not have changed. Also, there may have been redirects resulting in a different URL to that originally requested. - /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#getOriginalUrl() + ///Use [InAppWebViewController.getOriginalUrl] instead. + @Deprecated('Use `InAppWebViewController.getOriginalUrl` instead') Future getOriginalUrl() async { Map args = {}; String? url = await _channel.invokeMethod('getOriginalUrl', args); @@ -114,7 +111,8 @@ class AndroidInAppWebViewController { ///Clears the internal back/forward list. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#clearHistory() + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.clearHistory](https://developer.android.com/reference/android/webkit/WebView#clearHistory())). Future clearHistory() async { Map args = {}; return await _channel.invokeMethod('clearHistory', args); diff --git a/lib/src/in_app_webview/headless_in_app_webview.dart b/lib/src/in_app_webview/headless_in_app_webview.dart index f19b5152..232d4e6d 100644 --- a/lib/src/in_app_webview/headless_in_app_webview.dart +++ b/lib/src/in_app_webview/headless_in_app_webview.dart @@ -53,6 +53,7 @@ class HeadlessInAppWebView implements WebView { this.contextMenu, this.initialUserScripts, this.pullToRefreshController, + this.implementation = WebViewImplementation.NATIVE, this.onWebViewCreated, this.onLoadStart, this.onLoadStop, @@ -147,6 +148,7 @@ class HeadlessInAppWebView implements WebView { 'initialOptions': this.initialOptions?.toMap() ?? {}, 'contextMenu': this.contextMenu?.toMap() ?? {}, 'windowId': this.windowId, + 'implementation': this.implementation, 'initialUserScripts': this.initialUserScripts?.map((e) => e.toMap()).toList() ?? [], 'pullToRefreshOptions': @@ -227,6 +229,9 @@ class HeadlessInAppWebView implements WebView { @override final PullToRefreshController? pullToRefreshController; + @override + final WebViewImplementation implementation; + @override void Function(InAppWebViewController controller)? androidOnGeolocationPermissionsHidePrompt; diff --git a/lib/src/in_app_webview/in_app_webview.dart b/lib/src/in_app_webview/in_app_webview.dart index 7ef7ee2f..f60f956f 100755 --- a/lib/src/in_app_webview/in_app_webview.dart +++ b/lib/src/in_app_webview/in_app_webview.dart @@ -41,6 +41,7 @@ class InAppWebView extends StatefulWidget implements WebView { this.initialOptions, this.initialUserScripts, this.pullToRefreshController, + this.implementation = WebViewImplementation.NATIVE, this.contextMenu, this.onWebViewCreated, this.onLoadStart, @@ -135,6 +136,9 @@ class InAppWebView extends StatefulWidget implements WebView { @override final URLRequest? initialUrlRequest; + @override + final WebViewImplementation implementation; + @override final UnmodifiableListView? initialUserScripts; @@ -403,6 +407,7 @@ class _InAppWebViewState extends State { 'initialOptions': widget.initialOptions?.toMap() ?? {}, 'contextMenu': widget.contextMenu?.toMap() ?? {}, 'windowId': widget.windowId, + 'implementation': widget.implementation.toValue(), 'initialUserScripts': widget.initialUserScripts?.map((e) => e.toMap()).toList() ?? [], @@ -423,7 +428,7 @@ class _InAppWebViewState extends State { viewType: 'com.pichillilorenzo/flutter_inappwebview', onPlatformViewCreated: _onPlatformViewCreated, gestureRecognizers: widget.gestureRecognizers, - layoutDirection: TextDirection.rtl, + layoutDirection: Directionality.maybeOf(context) ?? TextDirection.rtl, creationParams: { 'initialUrlRequest': (widget.initialUrlRequest ?? URLRequest(url: Uri.parse("about:blank"))) @@ -433,6 +438,7 @@ class _InAppWebViewState extends State { 'initialOptions': widget.initialOptions?.toMap() ?? {}, 'contextMenu': widget.contextMenu?.toMap() ?? {}, 'windowId': widget.windowId, + 'implementation': widget.implementation.toValue(), 'initialUserScripts': widget.initialUserScripts?.map((e) => e.toMap()).toList() ?? [], 'pullToRefreshOptions': @@ -456,6 +462,7 @@ class _InAppWebViewState extends State { 'initialOptions': widget.initialOptions?.toMap() ?? {}, 'contextMenu': widget.contextMenu?.toMap() ?? {}, 'windowId': widget.windowId, + 'implementation': widget.implementation.toValue(), 'initialUserScripts': widget.initialUserScripts?.map((e) => e.toMap()).toList() ?? [], 'pullToRefreshOptions': diff --git a/lib/src/in_app_webview/in_app_webview_controller.dart b/lib/src/in_app_webview/in_app_webview_controller.dart index d34072bb..e6bb9048 100644 --- a/lib/src/in_app_webview/in_app_webview_controller.dart +++ b/lib/src/in_app_webview/in_app_webview_controller.dart @@ -904,9 +904,9 @@ class InAppWebViewController { ///Gets the URL for the current page. ///This is not always the same as the URL passed to [WebView.onLoadStart] because although the load for that URL has begun, the current page may not have changed. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#getUrl() - /// - ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkwebview/1415005-url + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.getUrl](https://developer.android.com/reference/android/webkit/WebView#getUrl())) + ///- iOS ([Official API - WKWebView.url](https://developer.apple.com/documentation/webkit/wkwebview/1415005-url)) Future getUrl() async { Map args = {}; String? url = await _channel.invokeMethod('getUrl', args); @@ -915,9 +915,9 @@ class InAppWebViewController { ///Gets the title for the current page. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#getTitle() - /// - ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkwebview/1415015-title + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.getTitle](https://developer.android.com/reference/android/webkit/WebView#getTitle())) + ///- iOS ([Official API - WKWebView.title](https://developer.apple.com/documentation/webkit/wkwebview/1415015-title)) Future getTitle() async { Map args = {}; return await _channel.invokeMethod('getTitle', args); @@ -925,9 +925,9 @@ class InAppWebViewController { ///Gets the progress for the current page. The progress value is between 0 and 100. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#getProgress() - /// - ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkwebview/1415007-estimatedprogress + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.getProgress](https://developer.android.com/reference/android/webkit/WebView#getProgress())) + ///- iOS ([Official API - WKWebView.estimatedProgress](https://developer.apple.com/documentation/webkit/wkwebview/1415007-estimatedprogress)) Future getProgress() async { Map args = {}; return await _channel.invokeMethod('getProgress', args); @@ -1138,21 +1138,20 @@ class InAppWebViewController { ///Loads the given [urlRequest]. /// - ///[iosAllowingReadAccessTo], used in combination with [urlRequest] (using the `file://` scheme), - ///is an iOS-specific argument that represents the URL from which to read the web content. + ///- [allowingReadAccessTo], used in combination with [urlRequest] (using the `file://` scheme), + ///it represents the URL from which to read the web content. ///This URL must be a file-based URL (using the `file://` scheme). ///Specify the same value as the URL parameter to prevent WebView from reading any other content. ///Specify a directory to give WebView permission to read additional files in the specified directory. + ///**NOTE**: available only on iOS. /// ///**NOTE for Android**: when loading an URL Request using "POST" method, headers are ignored. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#loadUrl(java.lang.String) - /// - ///**Official iOS API**: - ///- https://developer.apple.com/documentation/webkit/wkwebview/1414954-load - ///- if [iosAllowingReadAccessTo] is used, https://developer.apple.com/documentation/webkit/wkwebview/1414973-loadfileurl + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.loadUrl](https://developer.android.com/reference/android/webkit/WebView#loadUrl(java.lang.String))). If method is "POST", [Official API - WebView.postUrl](https://developer.android.com/reference/android/webkit/WebView#postUrl(java.lang.String,%20byte[])) + ///- iOS ([Official API - WKWebView.load](https://developer.apple.com/documentation/webkit/wkwebview/1414954-load). If [allowingReadAccessTo] is used, [Official API - WKWebView.loadFileURL](https://developer.apple.com/documentation/webkit/wkwebview/1414973-loadfileurl)) Future loadUrl( - {required URLRequest urlRequest, Uri? iosAllowingReadAccessTo}) async { + {required URLRequest urlRequest, @Deprecated('Use `allowingReadAccessTo` instead') Uri? iosAllowingReadAccessTo, Uri? allowingReadAccessTo}) async { assert(urlRequest.url != null && urlRequest.url.toString().isNotEmpty); assert(iosAllowingReadAccessTo == null || iosAllowingReadAccessTo.isScheme("file")); @@ -1160,19 +1159,21 @@ class InAppWebViewController { Map args = {}; args.putIfAbsent('urlRequest', () => urlRequest.toMap()); args.putIfAbsent( - 'allowingReadAccessTo', () => iosAllowingReadAccessTo?.toString()); + 'allowingReadAccessTo', () => allowingReadAccessTo?.toString() ?? iosAllowingReadAccessTo?.toString()); await _channel.invokeMethod('loadUrl', args); } ///Loads the given [url] with [postData] (x-www-form-urlencoded) using `POST` method into this WebView. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#postUrl(java.lang.String,%20byte[]) - /// ///Example ///```dart ///var postData = Uint8List.fromList(utf8.encode("firstname=Foo&surname=Bar")); ///controller.postUrl(url: Uri.parse("https://www.example.com/"), postData: postData); ///``` + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.postUrl](https://developer.android.com/reference/android/webkit/WebView#postUrl(java.lang.String,%20byte[]))) + ///- iOS Future postUrl({required Uri url, required Uint8List postData}) async { assert(url.toString().isNotEmpty); Map args = {}; @@ -1183,27 +1184,30 @@ class InAppWebViewController { ///Loads the given [data] into this WebView, using [baseUrl] as the base URL for the content. /// - ///- [mimeType] parameter specifies the format of the data. The default value is `"text/html"`. - ///- [encoding] parameter specifies the encoding of the data. The default value is `"utf8"`. - ///- [androidHistoryUrl] parameter is the URL to use as the history entry. The default value is `about:blank`. If non-null, this must be a valid URL. This parameter is used only on Android. - ///- [iosAllowingReadAccessTo], used in combination with [baseUrl] (using the `file://` scheme), - ///is an iOS-specific argument that represents the URL from which to read the web content. + ///- [mimeType] argument specifies the format of the data. The default value is `"text/html"`. + ///- [encoding] argument specifies the encoding of the data. The default value is `"utf8"`. + ///- [historyUrl] is an Android-specific argument that represents the URL to use as the history entry. The default value is `about:blank`. If non-null, this must be a valid URL. + ///- [allowingReadAccessTo], used in combination with [baseUrl] (using the `file://` scheme), + ///it represents the URL from which to read the web content. ///This [baseUrl] must be a file-based URL (using the `file://` scheme). ///Specify the same value as the [baseUrl] parameter to prevent WebView from reading any other content. ///Specify a directory to give WebView permission to read additional files in the specified directory. + ///**NOTE**: available only on iOS. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#loadDataWithBaseURL(java.lang.String,%20java.lang.String,%20java.lang.String,%20java.lang.String,%20java.lang.String) - /// - ///**Official iOS API**: - ///- https://developer.apple.com/documentation/webkit/wkwebview/1415004-loadhtmlstring - ///- https://developer.apple.com/documentation/webkit/wkwebview/1415011-load + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.loadDataWithBaseURL](https://developer.android.com/reference/android/webkit/WebView#loadDataWithBaseURL(java.lang.String,%20java.lang.String,%20java.lang.String,%20java.lang.String,%20java.lang.String))) + ///- iOS ([Official API - WKWebView.loadHTMLString](https://developer.apple.com/documentation/webkit/wkwebview/1415004-loadhtmlstring) or [Official API - WKWebView.load](https://developer.apple.com/documentation/webkit/wkwebview/1415011-load)) Future loadData( {required String data, String mimeType = "text/html", String encoding = "utf8", Uri? baseUrl, + @Deprecated('Use `historyUrl` instead') Uri? androidHistoryUrl, - Uri? iosAllowingReadAccessTo}) async { + Uri? historyUrl, + @Deprecated('Use `allowingReadAccessTo` instead') + Uri? iosAllowingReadAccessTo, + Uri? allowingReadAccessTo}) async { assert(iosAllowingReadAccessTo == null || iosAllowingReadAccessTo.isScheme("file")); @@ -1213,9 +1217,9 @@ class InAppWebViewController { args.putIfAbsent('encoding', () => encoding); args.putIfAbsent('baseUrl', () => baseUrl?.toString() ?? "about:blank"); args.putIfAbsent( - 'historyUrl', () => androidHistoryUrl?.toString() ?? "about:blank"); + 'historyUrl', () => historyUrl?.toString() ?? androidHistoryUrl?.toString() ?? "about:blank"); args.putIfAbsent( - 'allowingReadAccessTo', () => iosAllowingReadAccessTo?.toString()); + 'allowingReadAccessTo', () => allowingReadAccessTo?.toString() ?? iosAllowingReadAccessTo?.toString()); await _channel.invokeMethod('loadData', args); } @@ -1248,6 +1252,10 @@ class InAppWebViewController { ///controller.loadFile(assetFilePath: "assets/index.html"); ///... ///``` + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.loadUrl](https://developer.android.com/reference/android/webkit/WebView#loadUrl(java.lang.String))) + ///- iOS ([Official API - WKWebView.load](https://developer.apple.com/documentation/webkit/wkwebview/1414954-load)) Future loadFile({required String assetFilePath}) async { assert(assetFilePath.isNotEmpty); Map args = {}; @@ -1257,9 +1265,9 @@ class InAppWebViewController { ///Reloads the WebView. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#reload() - /// - ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkwebview/1414969-reload + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.reload](https://developer.android.com/reference/android/webkit/WebView#reload())) + ///- iOS ([Official API - WKWebView.reload](https://developer.apple.com/documentation/webkit/wkwebview/1414969-reload)) Future reload() async { Map args = {}; await _channel.invokeMethod('reload', args); @@ -1267,9 +1275,9 @@ class InAppWebViewController { ///Goes back in the history of the WebView. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#goBack() - /// - ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkwebview/1414952-goback + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.goBack](https://developer.android.com/reference/android/webkit/WebView#goBack())) + ///- iOS ([Official API - WKWebView.goBack](https://developer.apple.com/documentation/webkit/wkwebview/1414952-goback)) Future goBack() async { Map args = {}; await _channel.invokeMethod('goBack', args); @@ -1277,9 +1285,9 @@ class InAppWebViewController { ///Returns a boolean value indicating whether the WebView can move backward. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#canGoBack() - /// - ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkwebview/1414966-cangoback + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.canGoBack](https://developer.android.com/reference/android/webkit/WebView#canGoBack())) + ///- iOS ([Official API - WKWebView.canGoBack](https://developer.apple.com/documentation/webkit/wkwebview/1414966-cangoback)) Future canGoBack() async { Map args = {}; return await _channel.invokeMethod('canGoBack', args); @@ -1287,9 +1295,9 @@ class InAppWebViewController { ///Goes forward in the history of the WebView. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#goForward() - /// - ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkwebview/1414993-goforward + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.goForward](https://developer.android.com/reference/android/webkit/WebView#goForward())) + ///- iOS ([Official API - WKWebView.goForward](https://developer.apple.com/documentation/webkit/wkwebview/1414993-goforward)) Future goForward() async { Map args = {}; await _channel.invokeMethod('goForward', args); @@ -1297,9 +1305,9 @@ class InAppWebViewController { ///Returns a boolean value indicating whether the WebView can move forward. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#canGoForward() - /// - ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkwebview/1414962-cangoforward + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.canGoForward](https://developer.android.com/reference/android/webkit/WebView#canGoForward())) + ///- iOS ([Official API - WKWebView.canGoForward](https://developer.apple.com/documentation/webkit/wkwebview/1414962-cangoforward)) Future canGoForward() async { Map args = {}; return await _channel.invokeMethod('canGoForward', args); @@ -1307,9 +1315,9 @@ class InAppWebViewController { ///Goes to the history item that is the number of steps away from the current item. Steps is negative if backward and positive if forward. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#goBackOrForward(int) - /// - ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkwebview/1414991-go + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.goBackOrForward](https://developer.android.com/reference/android/webkit/WebView#goBackOrForward(int))) + ///- iOS ([Official API - WKWebView.go](https://developer.apple.com/documentation/webkit/wkwebview/1414991-go)) Future goBackOrForward({required int steps}) async { Map args = {}; args.putIfAbsent('steps', () => steps); @@ -1318,7 +1326,9 @@ class InAppWebViewController { ///Returns a boolean value indicating whether the WebView can go back or forward the given number of steps. Steps is negative if backward and positive if forward. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#canGoBackOrForward(int) + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.canGoBackOrForward](https://developer.android.com/reference/android/webkit/WebView#canGoBackOrForward(int))) + ///- iOS Future canGoBackOrForward({required int steps}) async { Map args = {}; args.putIfAbsent('steps', () => steps); @@ -1326,13 +1336,22 @@ class InAppWebViewController { } ///Navigates to a [WebHistoryItem] from the back-forward [WebHistory.list] and sets it as the current item. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS Future goTo({required WebHistoryItem historyItem}) async { - if (historyItem.offset != null) { - await goBackOrForward(steps: historyItem.offset!); + var steps = historyItem.offset; + if (steps != null) { + await goBackOrForward(steps: steps); } } ///Check if the WebView instance is in a loading state. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS Future isLoading() async { Map args = {}; return await _channel.invokeMethod('isLoading', args); @@ -1340,9 +1359,9 @@ class InAppWebViewController { ///Stops the WebView from loading. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#stopLoading() - /// - ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkwebview/1414981-stoploading + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.stopLoading](https://developer.android.com/reference/android/webkit/WebView#stopLoading())) + ///- iOS ([Official API - WKWebView.stopLoading](https://developer.apple.com/documentation/webkit/wkwebview/1414981-stoploading)) Future stopLoading() async { Map args = {}; await _channel.invokeMethod('stopLoading', args); @@ -1362,19 +1381,22 @@ class InAppWebViewController { ///Instead, you should call this method, for example, inside the [WebView.onLoadStop] event or in any other events ///where you know the page is ready "enough". /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#evaluateJavascript(java.lang.String,%20android.webkit.ValueCallback%3Cjava.lang.String%3E) - /// - ///**Official iOS API**: - ///- https://developer.apple.com/documentation/webkit/wkwebview/1415017-evaluatejavascript - ///- https://developer.apple.com/documentation/webkit/wkwebview/3656442-evaluatejavascript + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.evaluateJavascript](https://developer.android.com/reference/android/webkit/WebView#evaluateJavascript(java.lang.String,%20android.webkit.ValueCallback%3Cjava.lang.String%3E))) + ///- iOS ([Official API - WKWebView.evaluateJavascript](https://developer.apple.com/documentation/webkit/wkwebview/3656442-evaluatejavascript)) Future evaluateJavascript( {required String source, ContentWorld? contentWorld}) async { Map args = {}; args.putIfAbsent('source', () => source); args.putIfAbsent('contentWorld', () => contentWorld?.toMap()); var data = await _channel.invokeMethod('evaluateJavascript', args); - if (data != null && defaultTargetPlatform == TargetPlatform.android) - data = json.decode(data); + if (data != null && defaultTargetPlatform == TargetPlatform.android) { + try { + // try to json decode the data coming from JavaScript + // otherwise return it as it is. + data = json.decode(data); + } catch (e) {} + } return data; } @@ -1386,6 +1408,10 @@ class InAppWebViewController { ///because, in these events, the [WebView] is not ready to handle it yet. ///Instead, you should call this method, for example, inside the [WebView.onLoadStop] event or in any other events ///where you know the page is ready "enough". + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS Future injectJavascriptFileFromUrl( {required Uri urlFile, ScriptHtmlTagAttributes? scriptHtmlTagAttributes}) async { @@ -1407,6 +1433,10 @@ class InAppWebViewController { ///because, in these events, the [WebView] is not ready to handle it yet. ///Instead, you should call this method, for example, inside the [WebView.onLoadStop] event or in any other events ///where you know the page is ready "enough". + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS Future injectJavascriptFileFromAsset( {required String assetFilePath}) async { String source = await rootBundle.loadString(assetFilePath); @@ -1419,6 +1449,10 @@ class InAppWebViewController { ///because, in these events, the [WebView] is not ready to handle it yet. ///Instead, you should call this method, for example, inside the [WebView.onLoadStop] event or in any other events ///where you know the page is ready "enough". + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS Future injectCSSCode({required String source}) async { Map args = {}; args.putIfAbsent('source', () => source); @@ -1433,6 +1467,10 @@ class InAppWebViewController { ///because, in these events, the [WebView] is not ready to handle it yet. ///Instead, you should call this method, for example, inside the [WebView.onLoadStop] event or in any other events ///where you know the page is ready "enough". + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS Future injectCSSFileFromUrl( {required Uri urlFile, CSSLinkHtmlTagAttributes? cssLinkHtmlTagAttributes}) async { @@ -1450,6 +1488,10 @@ class InAppWebViewController { ///because, in these events, the [WebView] is not ready to handle it yet. ///Instead, you should call this method, for example, inside the [WebView.onLoadStop] event or in any other events ///where you know the page is ready "enough". + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS Future injectCSSFileFromAsset({required String assetFilePath}) async { String source = await rootBundle.loadString(assetFilePath); await injectCSSCode(source: source); @@ -1505,6 +1547,10 @@ class InAppWebViewController { ///**NOTE**: This method should be called, for example, in the [WebView.onWebViewCreated] or [WebView.onLoadStart] events or, at least, ///before you know that your JavaScript code will call the `window.flutter_inappwebview.callHandler` method, ///otherwise you won't be able to intercept the JavaScript message. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS void addJavaScriptHandler( {required String handlerName, required JavaScriptHandlerCallback callback}) { @@ -1516,6 +1562,10 @@ class InAppWebViewController { ///Removes a JavaScript message handler previously added with the [addJavaScriptHandler()] associated to [handlerName] key. ///Returns the value associated with [handlerName] before it was removed. ///Returns `null` if [handlerName] was not found. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS JavaScriptHandlerCallback? removeJavaScriptHandler( {required String handlerName}) { return this.javaScriptHandlersMap.remove(handlerName); @@ -1527,7 +1577,9 @@ class InAppWebViewController { /// ///**NOTE for iOS**: available on iOS 11.0+. /// - ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkwebview/2873260-takesnapshot + ///**Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS ([Official API - WKWebView.takeSnapshot](https://developer.apple.com/documentation/webkit/wkwebview/2873260-takesnapshot)) Future takeScreenshot( {ScreenshotConfiguration? screenshotConfiguration}) async { Map args = {}; @@ -1537,6 +1589,10 @@ class InAppWebViewController { } ///Sets the WebView options with the new [options] and evaluates them. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS Future setOptions({required InAppWebViewGroupOptions options}) async { Map args = {}; @@ -1545,6 +1601,10 @@ class InAppWebViewController { } ///Gets the current WebView options. Returns `null` if it wasn't able to get them. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS Future getOptions() async { Map args = {}; @@ -1563,9 +1623,9 @@ class InAppWebViewController { ///Multiple calls to this method may return different objects. ///The object returned from this method will not be updated to reflect any new state. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#copyBackForwardList() - /// - ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkwebview/1414977-backforwardlist + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.copyBackForwardList](https://developer.android.com/reference/android/webkit/WebView#copyBackForwardList())) + ///- iOS ([Official API - WKWebView.backForwardList](https://developer.apple.com/documentation/webkit/wkwebview/1414977-backforwardlist)) Future getCopyBackForwardList() async { Map args = {}; Map? result = @@ -1574,7 +1634,11 @@ class InAppWebViewController { return WebHistory.fromMap(result); } - ///Clears all the webview's cache. + ///Clears all the WebView's cache. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS Future clearCache() async { Map args = {}; await _channel.invokeMethod('clearCache', args); @@ -1584,24 +1648,28 @@ class InAppWebViewController { /// ///[find] represents the string to find. /// - ///**NOTE**: on Android, it finds all instances asynchronously. Successive calls to this will cancel any pending searches. + ///**NOTE**: on Android native WebView, it finds all instances asynchronously. Successive calls to this will cancel any pending searches. /// ///**NOTE**: on iOS, this is implemented using CSS and Javascript. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#findAllAsync(java.lang.String) + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.findAllAsync](https://developer.android.com/reference/android/webkit/WebView#findAllAsync(java.lang.String))) + ///- iOS Future findAllAsync({required String find}) async { Map args = {}; args.putIfAbsent('find', () => find); await _channel.invokeMethod('findAllAsync', args); } - ///Highlights and scrolls to the next match found by [findAllAsync()]. Notifies [WebView.onFindResultReceived] listener. + ///Highlights and scrolls to the next match found by [findAllAsync]. Notifies [WebView.onFindResultReceived] listener. /// ///[forward] represents the direction to search. /// ///**NOTE**: on iOS, this is implemented using CSS and Javascript. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#findNext(boolean) + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.findNext](https://developer.android.com/reference/android/webkit/WebView#findNext(boolean))) + ///- iOS Future findNext({required bool forward}) async { Map args = {}; args.putIfAbsent('forward', () => forward); @@ -1612,22 +1680,32 @@ class InAppWebViewController { /// ///**NOTE**: on iOS, this is implemented using CSS and Javascript. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#clearMatches() + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.clearMatches](https://developer.android.com/reference/android/webkit/WebView#clearMatches())) + ///- iOS Future clearMatches() async { Map args = {}; await _channel.invokeMethod('clearMatches', args); } - ///Gets the html (with javascript) of the Chromium's t-rex runner game. Used in combination with [getTRexRunnerCss()]. + ///Gets the html (with javascript) of the Chromium's t-rex runner game. Used in combination with [getTRexRunnerCss]. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS Future getTRexRunnerHtml() async { return await rootBundle - .loadString("packages/flutter_inappwebview/t_rex_runner/t-rex.html"); + .loadString("packages/flutter_inappwebview/assets/t_rex_runner/t-rex.html"); } - ///Gets the css of the Chromium's t-rex runner game. Used in combination with [getTRexRunnerHtml()]. + ///Gets the css of the Chromium's t-rex runner game. Used in combination with [getTRexRunnerHtml]. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS Future getTRexRunnerCss() async { return await rootBundle - .loadString("packages/flutter_inappwebview/t_rex_runner/t-rex.css"); + .loadString("packages/flutter_inappwebview/assets/t_rex_runner/t-rex.css"); } ///Scrolls the WebView to the position. @@ -1638,9 +1716,9 @@ class InAppWebViewController { /// ///[animated] `true` to animate the scroll transition, `false` to make the scoll transition immediate. /// - ///**Official Android API**: https://developer.android.com/reference/android/view/View#scrollTo(int,%20int) - /// - ///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiscrollview/1619400-setcontentoffset + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - View.scrollTo](https://developer.android.com/reference/android/view/View#scrollTo(int,%20int))) + ///- iOS ([Official API - UIScrollView.setContentOffset](https://developer.apple.com/documentation/uikit/uiscrollview/1619400-setcontentoffset)) Future scrollTo( {required int x, required int y, bool animated = false}) async { Map args = {}; @@ -1658,9 +1736,9 @@ class InAppWebViewController { /// ///[animated] `true` to animate the scroll transition, `false` to make the scoll transition immediate. /// - ///**Official Android API**: https://developer.android.com/reference/android/view/View#scrollBy(int,%20int) - /// - ///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiscrollview/1619400-setcontentoffset + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - View.scrollBy](https://developer.android.com/reference/android/view/View#scrollBy(int,%20int))) + ///- iOS ([Official API - UIScrollView.setContentOffset](https://developer.apple.com/documentation/uikit/uiscrollview/1619400-setcontentoffset)) Future scrollBy( {required int x, required int y, bool animated = false}) async { Map args = {}; @@ -1670,12 +1748,14 @@ class InAppWebViewController { await _channel.invokeMethod('scrollBy', args); } - ///On Android, it pauses all layout, parsing, and JavaScript timers for all WebViews. + ///On Android native WebView, it pauses all layout, parsing, and JavaScript timers for all WebViews. ///This is a global requests, not restricted to just this WebView. This can be useful if the application has been paused. /// - ///On iOS, it is restricted to just this WebView. + ///On iOS, it is implemented using JavaScript and it is restricted to just this WebView. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#pauseTimers() + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.pauseTimers](https://developer.android.com/reference/android/webkit/WebView#pauseTimers())) + ///- iOS Future pauseTimers() async { Map args = {}; await _channel.invokeMethod('pauseTimers', args); @@ -1683,9 +1763,11 @@ class InAppWebViewController { ///On Android, it resumes all layout, parsing, and JavaScript timers for all WebViews. This will resume dispatching all timers. /// - ///On iOS, it resumes all layout, parsing, and JavaScript timers to just this WebView. + ///On iOS, it is implemented using JavaScript and it resumes all layout, parsing, and JavaScript timers to just this WebView. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#resumeTimers() + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.resumeTimers](https://developer.android.com/reference/android/webkit/WebView#resumeTimers())) + ///- iOS Future resumeTimers() async { Map args = {}; await _channel.invokeMethod('resumeTimers', args); @@ -1695,9 +1777,9 @@ class InAppWebViewController { /// ///**NOTE**: available on Android 21+. /// - ///**Official Android API**: https://developer.android.com/reference/android/print/PrintManager - /// - ///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiprintinteractioncontroller + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - PrintManager](https://developer.android.com/reference/android/print/PrintManager)) + ///- iOS ([Official API - UIPrintInteractionController](https://developer.apple.com/documentation/uikit/uiprintinteractioncontroller)) Future printCurrentPage() async { Map args = {}; await _channel.invokeMethod('printCurrentPage', args); @@ -1705,9 +1787,9 @@ class InAppWebViewController { ///Gets the height of the HTML content. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#getContentHeight() - /// - ///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiscrollview/1619399-contentsize + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.getContentHeight](https://developer.android.com/reference/android/webkit/WebView#getContentHeight())) + ///- iOS ([Official API - UIScrollView.contentSize](https://developer.apple.com/documentation/uikit/uiscrollview/1619399-contentsize)) Future getContentHeight() async { Map args = {}; return await _channel.invokeMethod('getContentHeight', args); @@ -1717,16 +1799,16 @@ class InAppWebViewController { /// ///[zoomFactor] represents the zoom factor to apply. On Android, the zoom factor will be clamped to the Webview's zoom limits and, also, this value must be in the range 0.01 (excluded) to 100.0 (included). /// - ///[iosAnimated] `true` to animate the transition to the new scale, `false` to make the transition immediate. + ///[animated] `true` to animate the transition to the new scale, `false` to make the transition immediate. ///**NOTE**: available only on iOS. /// ///**NOTE**: available on Android 21+. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#zoomBy(float) - /// - ///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiscrollview/1619412-setzoomscale + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.zoomBy](https://developer.android.com/reference/android/webkit/WebView#zoomBy(float))) + ///- iOS ([Official API - UIScrollView.setZoomScale](https://developer.apple.com/documentation/uikit/uiscrollview/1619412-setzoomscale)) Future zoomBy( - {required double zoomFactor, bool iosAnimated = false}) async { + {required double zoomFactor, @Deprecated('Use `animated` instead') bool? iosAnimated, bool animated = false}) async { assert(defaultTargetPlatform != TargetPlatform.android || (defaultTargetPlatform == TargetPlatform.android && zoomFactor > 0.01 && @@ -1734,13 +1816,28 @@ class InAppWebViewController { Map args = {}; args.putIfAbsent('zoomFactor', () => zoomFactor); - args.putIfAbsent('iosAnimated', () => iosAnimated); + args.putIfAbsent('animated', () => iosAnimated ?? animated); return await _channel.invokeMethod('zoomBy', args); } + ///Gets the URL that was originally requested for the current page. + ///This is not always the same as the URL passed to [InAppWebView.onLoadStarted] because although the load for that URL has begun, + ///the current page may not have changed. Also, there may have been redirects resulting in a different URL to that originally requested. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.getOriginalUrl](https://developer.android.com/reference/android/webkit/WebView#getOriginalUrl())) + ///- iOS + Future getOriginalUrl() async { + Map args = {}; + String? url = await _channel.invokeMethod('getOriginalUrl', args); + return url != null ? Uri.parse(url) : null; + } + ///Gets the current zoom scale of the WebView. /// - ///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiscrollview/1619419-zoomscale + ///**Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS ([Official API - UIScrollView.zoomScale](https://developer.apple.com/documentation/uikit/uiscrollview/1619419-zoomscale)) Future getZoomScale() async { Map args = {}; return await _channel.invokeMethod('getZoomScale', args); @@ -1754,9 +1851,13 @@ class InAppWebViewController { ///Gets the selected text. /// - ///**NOTE**: This method is implemented with using JavaScript. + ///**NOTE**: this method is implemented with using JavaScript. /// - ///**NOTE for Android**: available only on Android 19+. + ///**NOTE for Android native WebView**: available only on Android 19+. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS Future getSelectedText() async { Map args = {}; return await _channel.invokeMethod('getSelectedText', args); @@ -1764,9 +1865,11 @@ class InAppWebViewController { ///Gets the hit result for hitting an HTML elements. /// - ///**NOTE**: On iOS it is implemented using JavaScript. + ///**NOTE**: On iOS, it is implemented using JavaScript. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#getHitTestResult() + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.getHitTestResult](https://developer.android.com/reference/android/webkit/WebView#getHitTestResult())) + ///- iOS Future getHitTestResult() async { Map args = {}; Map? hitTestResultMap = @@ -1785,11 +1888,11 @@ class InAppWebViewController { return InAppWebViewHitTestResult(type: type, extra: extra); } - ///Clears the current focus. It will clear also, for example, the current text selection. + ///Clears the current focus. On iOS and Android native WebView, it will clear also, for example, the current text selection. /// - ///**Official Android API**: https://developer.android.com/reference/android/view/ViewGroup#clearFocus() - /// - ///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiresponder/1621097-resignfirstresponder + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - ViewGroup.clearFocus](https://developer.android.com/reference/android/view/ViewGroup#clearFocus())) + ///- iOS ([Official API - UIResponder.resignFirstResponder](https://developer.apple.com/documentation/uikit/uiresponder/1621097-resignfirstresponder)) Future clearFocus() async { Map args = {}; return await _channel.invokeMethod('clearFocus', args); @@ -1805,9 +1908,11 @@ class InAppWebViewController { ///Requests the anchor or image element URL at the last tapped point. /// - ///**NOTE**: On iOS it is implemented using JavaScript. + ///**NOTE**: On iOS, it is implemented using JavaScript. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#requestFocusNodeHref(android.os.Message) + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.requestFocusNodeHref](https://developer.android.com/reference/android/webkit/WebView#requestFocusNodeHref(android.os.Message))). + ///- iOS. Future requestFocusNodeHref() async { Map args = {}; Map? result = @@ -1823,9 +1928,11 @@ class InAppWebViewController { ///Requests the URL of the image last touched by the user. /// - ///**NOTE**: On iOS it is implemented using JavaScript. + ///**NOTE**: On iOS, it is implemented using JavaScript. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#requestImageRef(android.os.Message) + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.requestImageRef](https://developer.android.com/reference/android/webkit/WebView#requestImageRef(android.os.Message))). + ///- iOS. Future requestImageRef() async { Map args = {}; Map? result = @@ -1947,7 +2054,9 @@ class InAppWebViewController { ///Gets the SSL certificate for the main top-level page or null if there is no certificate (the site is not secure). /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#getCertificate() + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.getCertificate](https://developer.android.com/reference/android/webkit/WebView#getCertificate())). + ///- iOS. Future getCertificate() async { Map args = {}; Map? sslCertificateMap = @@ -2155,11 +2264,13 @@ class InAppWebViewController { /// ///This method should be called when the page is loaded, for example, when the [WebView.onLoadStop] is fired, otherwise the [WebMessageChannel] won't work. /// - ///**NOTE for Android**: This method should only be called if [AndroidWebViewFeature.isFeatureSupported] returns `true` for [AndroidWebViewFeature.CREATE_WEB_MESSAGE_CHANNEL]. + ///**NOTE for Android native WebView**: This method should only be called if [AndroidWebViewFeature.isFeatureSupported] returns `true` for [AndroidWebViewFeature.CREATE_WEB_MESSAGE_CHANNEL]. /// - ///**NOTE for iOS**: This is implemented using Javascript. + ///**NOTE**: On iOS, it is implemented using JavaScript. /// - ///**Official Android API**: https://developer.android.com/reference/androidx/webkit/WebViewCompat#createWebMessageChannel(android.webkit.WebView) + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewCompat.createWebMessageChannel](https://developer.android.com/reference/androidx/webkit/WebViewCompat#createWebMessageChannel(android.webkit.WebView))). + ///- iOS. Future createWebMessageChannel() async { Map args = {}; Map? result = @@ -2173,11 +2284,13 @@ class InAppWebViewController { /// ///A target origin can be set as a wildcard ("*"). However this is not recommended. /// - ///**NOTE for Android**: This method should only be called if [AndroidWebViewFeature.isFeatureSupported] returns `true` for [AndroidWebViewFeature.POST_WEB_MESSAGE]. + ///**NOTE for Android native WebView**: This method should only be called if [AndroidWebViewFeature.isFeatureSupported] returns `true` for [AndroidWebViewFeature.POST_WEB_MESSAGE]. /// - ///**NOTE for iOS**: This is implemented using Javascript. + ///**NOTE**: On iOS, it is implemented using JavaScript. /// - ///**Official Android API**: https://developer.android.com/reference/androidx/webkit/WebViewCompat#postWebMessage(android.webkit.WebView,%20androidx.webkit.WebMessageCompat,%20android.net.Uri) + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewCompat.postWebMessage](https://developer.android.com/reference/androidx/webkit/WebViewCompat#postWebMessage(android.webkit.WebView,%20androidx.webkit.WebMessageCompat,%20android.net.Uri))). + ///- iOS. Future postWebMessage( {required WebMessage message, Uri? targetOrigin}) async { if (targetOrigin == null) { diff --git a/lib/src/in_app_webview/webview.dart b/lib/src/in_app_webview/webview.dart index c483acf1..3a1fa650 100644 --- a/lib/src/in_app_webview/webview.dart +++ b/lib/src/in_app_webview/webview.dart @@ -22,16 +22,16 @@ abstract class WebView { ///Event fired when the [WebView] starts to load an [url]. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onPageStarted(android.webkit.WebView,%20java.lang.String,%20android.graphics.Bitmap) - /// - ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455621-webview + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.onPageStarted](https://developer.android.com/reference/android/webkit/WebViewClient#onPageStarted(android.webkit.WebView,%20java.lang.String,%20android.graphics.Bitmap))) + ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455621-webview)) final void Function(InAppWebViewController controller, Uri? url)? onLoadStart; ///Event fired when the [WebView] finishes loading an [url]. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onPageFinished(android.webkit.WebView,%20java.lang.String) - /// - ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455629-webview + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.onPageFinished](https://developer.android.com/reference/android/webkit/WebViewClient#onPageFinished(android.webkit.WebView,%20java.lang.String))) + ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455629-webview)) final void Function(InAppWebViewController controller, Uri? url)? onLoadStop; ///Event fired when the [WebView] encounters an error loading an [url]. @@ -60,7 +60,9 @@ abstract class WebView { ///Event fired when the current [progress] of loading a page is changed. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onProgressChanged(android.webkit.WebView,%20int) + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebChromeClient.onProgressChanged](https://developer.android.com/reference/android/webkit/WebChromeClient#onProgressChanged(android.webkit.WebView,%20int))) + ///- iOS final void Function(InAppWebViewController controller, int progress)? onProgressChanged; @@ -254,7 +256,7 @@ abstract class WebView { onReceivedClientCertRequest; ///Event fired as find-on-page operations progress. - ///The listener may be notified multiple times while the operation is underway, and the numberOfMatches value should not be considered final unless [isDoneCounting] is true. + ///The listener may be notified multiple times while the operation is underway, and the [numberOfMatches] value should not be considered final unless [isDoneCounting] is true. /// ///[activeMatchOrdinal] represents the zero-based ordinal of the currently selected match. /// @@ -262,7 +264,9 @@ abstract class WebView { /// ///[isDoneCounting] whether the find operation has actually completed. /// - ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#setFindListener(android.webkit.WebView.FindListener) + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.FindListener.onFindResultReceived](https://developer.android.com/reference/android/webkit/WebView.FindListener#onFindResultReceived(int,%20int,%20boolean))) + ///- iOS final void Function(InAppWebViewController controller, int activeMatchOrdinal, int numberOfMatches, bool isDoneCounting)? onFindResultReceived; @@ -340,7 +344,9 @@ abstract class WebView { /// ///[url] represents the url on which is called. /// - ///**NOTE**: available on Android 21+. + ///**Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS final void Function(InAppWebViewController controller, Uri? url)? onPrint; ///Event fired when an HTML element of the webview has been clicked and held. @@ -674,6 +680,10 @@ abstract class WebView { ///**NOTE for Android**: to be able to use the "pull-to-refresh" feature, [AndroidInAppWebViewOptions.useHybridComposition] must be `true`. final PullToRefreshController? pullToRefreshController; + ///Represents the WebView native implementation to be used. + ///The default value is [WebViewImplementation.NATIVE]. + final WebViewImplementation implementation; + WebView( {this.windowId, this.onWebViewCreated, @@ -722,7 +732,7 @@ abstract class WebView { this.androidOnRenderProcessUnresponsive, this.androidOnFormResubmission, @Deprecated('Use `onZoomScaleChanged` instead') - this.androidOnScaleChanged, + this.androidOnScaleChanged, this.androidOnReceivedIcon, this.androidOnReceivedTouchIconUrl, this.androidOnJsBeforeUnload, @@ -737,5 +747,6 @@ abstract class WebView { this.initialOptions, this.contextMenu, this.initialUserScripts, - this.pullToRefreshController}); + this.pullToRefreshController, + this.implementation = WebViewImplementation.NATIVE}); } diff --git a/lib/src/types.dart b/lib/src/types.dart index eb9d1d68..12ecc54d 100755 --- a/lib/src/types.dart +++ b/lib/src/types.dart @@ -6850,3 +6850,51 @@ class AndroidPullToRefreshSize { @override int get hashCode => _value.hashCode; } + +///Class that represents the [WebView] native implementation to be used. +class WebViewImplementation { + final int _value; + + const WebViewImplementation._internal(this._value); + + static final Set values = [ + WebViewImplementation.NATIVE, + WebViewImplementation.GECKO, + ].toSet(); + + static WebViewImplementation? fromValue(int? value) { + if (value != null) { + try { + return WebViewImplementation.values + .firstWhere((element) => element.toValue() == value); + } catch (e) { + return null; + } + } + return null; + } + + int toValue() => _value; + + @override + String toString() { + switch (_value) { + case 1: + return "GECKO"; + case 0: + default: + return "NATIVE"; + } + } + + ///Default native implementation, such as `WKWebView` for iOS and `android.webkit.WebView` for Android. + static const NATIVE = const WebViewImplementation._internal(0); + + ///Android-only WebView implementation using the Mozilla's Gecko browser engine. + static const GECKO = const WebViewImplementation._internal(1); + + bool operator ==(value) => value == _value; + + @override + int get hashCode => _value.hashCode; +} \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index f810981b..45162ede 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_inappwebview description: A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window. -version: 5.3.2 +version: 5.3.3 homepage: https://github.com/pichillilorenzo/flutter_inappwebview environment: @@ -30,8 +30,8 @@ flutter: pluginClass: InAppWebViewFlutterPlugin assets: - - packages/flutter_inappwebview/t_rex_runner/t-rex.html - - packages/flutter_inappwebview/t_rex_runner/t-rex.css + - packages/flutter_inappwebview/assets/t_rex_runner/t-rex.html + - packages/flutter_inappwebview/assets/t_rex_runner/t-rex.css # To add assets to your plugin package, add an assets section, like this: # assets: # - images/a_dot_burr.jpeg From 099223a24e8ba15248376b4eba6613b2f455f3f2 Mon Sep 17 00:00:00 2001 From: Lorenzo Pichilli Date: Fri, 15 Apr 2022 20:16:10 +0200 Subject: [PATCH 08/11] Update Options.swift --- ios/Classes/Options.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/Classes/Options.swift b/ios/Classes/Options.swift index 96ba50eb..08f4976e 100755 --- a/ios/Classes/Options.swift +++ b/ios/Classes/Options.swift @@ -16,7 +16,7 @@ public class Options: NSObject { func parse(options: [String: Any?]) -> Options { for (key, value) in options { - if !(value is NSNull) { + if value != nil, !(value is NSNull) { if self.responds(to: Selector(key)) { self.setValue(value, forKey: key) } From af460d674659157acbdcbb8d5d0859b98f31d749 Mon Sep 17 00:00:00 2001 From: Lorenzo Pichilli Date: Fri, 15 Apr 2022 20:35:23 +0200 Subject: [PATCH 09/11] Update InAppWebView.swift removed setting `applicationNameForUserAgent` property from `prepare` method. --- ios/Classes/InAppWebView/InAppWebView.swift | 3 --- 1 file changed, 3 deletions(-) diff --git a/ios/Classes/InAppWebView/InAppWebView.swift b/ios/Classes/InAppWebView/InAppWebView.swift index 8ad01295..23f2fcda 100755 --- a/ios/Classes/InAppWebView/InAppWebView.swift +++ b/ios/Classes/InAppWebView/InAppWebView.swift @@ -405,9 +405,6 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi if #available(iOS 9.0, *) { configuration.allowsAirPlayForMediaPlayback = options.allowsAirPlayForMediaPlayback configuration.allowsPictureInPictureMediaPlayback = options.allowsPictureInPictureMediaPlayback - if !options.applicationNameForUserAgent.isEmpty { - configuration.applicationNameForUserAgent = options.applicationNameForUserAgent - } } configuration.preferences.javaScriptCanOpenWindowsAutomatically = options.javaScriptCanOpenWindowsAutomatically From 710fc1e02156c76163217d2c5bf9a2e10f74e722 Mon Sep 17 00:00:00 2001 From: Lorenzo Pichilli Date: Fri, 15 Apr 2022 23:18:54 +0200 Subject: [PATCH 10/11] Added singleInstance option for Android ChromeSafariBrowser implementation, updated android compileSdkVersion to 31 --- .fvm/flutter_sdk | 1 + .fvm/fvm_config.json | 4 +++ CHANGELOG.md | 5 +++ android/build.gradle | 2 +- android/src/main/AndroidManifest.xml | 4 +++ .../flutter_inappwebview/Util.java | 4 +++ ...hromeCustomTabsActivitySingleInstance.java | 7 ++++ .../ChromeSafariBrowserManager.java | 4 ++- example/.flutter-plugins-dependencies | 2 +- example/android/app/build.gradle | 4 +-- .../android/app/src/main/AndroidManifest.xml | 33 +++++++++---------- .../ios/Flutter/flutter_export_environment.sh | 2 +- example/ios/Runner.xcodeproj/project.pbxproj | 4 +-- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- .../chrome_safari_browser_example.screen.dart | 1 + example/lib/in_app_webiew_example.screen.dart | 2 +- flutter_inappwebview.iml | 6 ++++ .../android/chrome_custom_tabs_options.dart | 10 ++++-- lib/src/types.dart | 8 +---- 19 files changed, 69 insertions(+), 36 deletions(-) create mode 120000 .fvm/flutter_sdk create mode 100644 .fvm/fvm_config.json create mode 100755 android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ChromeCustomTabsActivitySingleInstance.java diff --git a/.fvm/flutter_sdk b/.fvm/flutter_sdk new file mode 120000 index 00000000..c6e3b15b --- /dev/null +++ b/.fvm/flutter_sdk @@ -0,0 +1 @@ +/Users/lorenzopichilli/fvm/versions/2.10.4 \ No newline at end of file diff --git a/.fvm/fvm_config.json b/.fvm/fvm_config.json new file mode 100644 index 00000000..d2faf497 --- /dev/null +++ b/.fvm/fvm_config.json @@ -0,0 +1,4 @@ +{ + "flutterSdkVersion": "2.10.4", + "flavors": {} +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index c2d8c81b..c49890d4 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,12 @@ ## 5.3.3 - `getOriginalUrl` method is cross-platform now +- Updated Android `compileSdkVersion` to 31 +- Added `singleInstance` option for Android `ChromeSafariBrowser` implementation - Fixed `requestImageRef` method always `null` on iOS +- Fixed "applicationNameForUserAgent is not work in ios" [#525](https://github.com/pichillilorenzo/flutter_inappwebview/issues/525) +- Merge "Fix parsing crash on null value." [#828](https://github.com/pichillilorenzo/flutter_inappwebview/pull/828) (thanks to [ItsCalebJones](https://github.com/ItsCalebJones)) +- Merge "fix: ApplicationNameForUserAgent is not working in iOS" [#1095](https://github.com/pichillilorenzo/flutter_inappwebview/pull/1095) (thanks to [sunalwaysknows](https://github.com/sunalwaysknows)) ## 5.3.2 diff --git a/android/build.gradle b/android/build.gradle index a5e831c4..443ec147 100755 --- a/android/build.gradle +++ b/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 30 + compileSdkVersion 31 defaultConfig { minSdkVersion 17 diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 40dc1267..c6ffe08e 100755 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -9,6 +9,10 @@ + @@ -36,22 +35,22 @@ - - - - + + + + + + + + + + + + + + + + { InAppWebViewGroupOptions options = InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( useShouldOverrideUrlLoading: true, - mediaPlaybackRequiresUserGesture: false, + mediaPlaybackRequiresUserGesture: false ), android: AndroidInAppWebViewOptions( useHybridComposition: true, diff --git a/flutter_inappwebview.iml b/flutter_inappwebview.iml index 0adae5aa..4f3a5faa 100755 --- a/flutter_inappwebview.iml +++ b/flutter_inappwebview.iml @@ -5,6 +5,12 @@ + + + + + + diff --git a/lib/src/chrome_safari_browser/android/chrome_custom_tabs_options.dart b/lib/src/chrome_safari_browser/android/chrome_custom_tabs_options.dart index d6901bb8..1fb97365 100755 --- a/lib/src/chrome_safari_browser/android/chrome_custom_tabs_options.dart +++ b/lib/src/chrome_safari_browser/android/chrome_custom_tabs_options.dart @@ -35,6 +35,9 @@ class AndroidChromeCustomTabsOptions ///Set to `true` to enable Keep Alive. The default value is `false`. bool keepAliveEnabled; + ///Set to `true` to launch the Android activity in `singleInstance` mode. The default value is `false`. + bool singleInstance; + AndroidChromeCustomTabsOptions( {this.addDefaultShareMenuItem = true, this.showTitle = true, @@ -42,7 +45,8 @@ class AndroidChromeCustomTabsOptions this.enableUrlBarHiding = false, this.instantAppsEnabled = false, this.packageName, - this.keepAliveEnabled = false}); + this.keepAliveEnabled = false, + this.singleInstance = false}); @override Map toMap() { @@ -53,7 +57,8 @@ class AndroidChromeCustomTabsOptions "enableUrlBarHiding": enableUrlBarHiding, "instantAppsEnabled": instantAppsEnabled, "packageName": packageName, - "keepAliveEnabled": keepAliveEnabled + "keepAliveEnabled": keepAliveEnabled, + "singleInstance": singleInstance }; } @@ -68,6 +73,7 @@ class AndroidChromeCustomTabsOptions options.instantAppsEnabled = map["instantAppsEnabled"]; options.packageName = map["packageName"]; options.keepAliveEnabled = map["keepAliveEnabled"]; + options.singleInstance = map["singleInstance"]; return options; } diff --git a/lib/src/types.dart b/lib/src/types.dart index 12ecc54d..96bf9d7e 100755 --- a/lib/src/types.dart +++ b/lib/src/types.dart @@ -6858,8 +6858,7 @@ class WebViewImplementation { const WebViewImplementation._internal(this._value); static final Set values = [ - WebViewImplementation.NATIVE, - WebViewImplementation.GECKO, + WebViewImplementation.NATIVE ].toSet(); static WebViewImplementation? fromValue(int? value) { @@ -6879,8 +6878,6 @@ class WebViewImplementation { @override String toString() { switch (_value) { - case 1: - return "GECKO"; case 0: default: return "NATIVE"; @@ -6890,9 +6887,6 @@ class WebViewImplementation { ///Default native implementation, such as `WKWebView` for iOS and `android.webkit.WebView` for Android. static const NATIVE = const WebViewImplementation._internal(0); - ///Android-only WebView implementation using the Mozilla's Gecko browser engine. - static const GECKO = const WebViewImplementation._internal(1); - bool operator ==(value) => value == _value; @override From 6e5669fb6848331d85a88816744381688201ac8e Mon Sep 17 00:00:00 2001 From: Lorenzo Pichilli Date: Fri, 15 Apr 2022 23:45:17 +0200 Subject: [PATCH 11/11] updated changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c49890d4..f1698933 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,13 @@ - Added `singleInstance` option for Android `ChromeSafariBrowser` implementation - Fixed `requestImageRef` method always `null` on iOS - Fixed "applicationNameForUserAgent is not work in ios" [#525](https://github.com/pichillilorenzo/flutter_inappwebview/issues/525) +- Fixed "Crash when try select file from webview input on Android" [#867](https://github.com/pichillilorenzo/flutter_inappwebview/issues/867) +- Fixed "NavigationAction.request should use toMap method" [#878](https://github.com/pichillilorenzo/flutter_inappwebview/issues/878) - Merge "Fix parsing crash on null value." [#828](https://github.com/pichillilorenzo/flutter_inappwebview/pull/828) (thanks to [ItsCalebJones](https://github.com/ItsCalebJones)) - Merge "fix: ApplicationNameForUserAgent is not working in iOS" [#1095](https://github.com/pichillilorenzo/flutter_inappwebview/pull/1095) (thanks to [sunalwaysknows](https://github.com/sunalwaysknows)) +- Merge "Make sure we open a new instance of a custom chrome chrome tab" [#812](https://github.com/pichillilorenzo/flutter_inappwebview/pull/812) (thanks to [savy-91](https://github.com/savy-91)) +- Merge "fix bug when in String[] array come null" [#868](https://github.com/pichillilorenzo/flutter_inappwebview/pull/868) (thanks to [Ser1ous](https://github.com/Ser1ous)) +- Merge "fix: use in NavigationAction request toMap method" [#879](https://github.com/pichillilorenzo/flutter_inappwebview/pull/879) (thanks to [chreck](https://github.com/chreck)) ## 5.3.2