Merge branch 'master' into feature/android-11

This commit is contained in:
Lorenzo Pichilli 2021-01-29 01:15:53 +01:00 committed by GitHub
commit 57557407a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
95 changed files with 4279 additions and 4541 deletions

View File

@ -1,16 +1,32 @@
## 5.0.0 ## 5.0.0-nullsafety.0
- Added support for Dart null-safety feature
- Added Android Hybrid Composition support "Use PlatformViewLink widget for Android WebView" [#462](https://github.com/pichillilorenzo/flutter_inappwebview/pull/462) (thanks to [plateaukao](https://github.com/plateaukao) and [tneotia](https://github.com/tneotia)) - Added Android Hybrid Composition support "Use PlatformViewLink widget for Android WebView" [#462](https://github.com/pichillilorenzo/flutter_inappwebview/pull/462) (thanks to [plateaukao](https://github.com/plateaukao) and [tneotia](https://github.com/tneotia))
- Added `allowUniversalAccessFromFileURLs` and `allowFileAccessFromFileURLs` WebView options also for iOS (also thanks to [liranhao](https://github.com/liranhao))
- 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 "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)) - 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))
- Merge "Fix: added iOS fallback module import" [#466](https://github.com/pichillilorenzo/flutter_inappwebview/pull/466) (thanks to [Eddayy](https://github.com/Eddayy)) - Merge "Fix: added iOS fallback module import" [#466](https://github.com/pichillilorenzo/flutter_inappwebview/pull/466) (thanks to [Eddayy](https://github.com/Eddayy))
- Merge "Fix NullPointerException after taking a photo by a camera app on Android" [#492](https://github.com/pichillilorenzo/flutter_inappwebview/pull/492) (thanks to [AAkira](https://github.com/AAkira)) - Merge "Fix NullPointerException after taking a photo by a camera app on Android" [#492](https://github.com/pichillilorenzo/flutter_inappwebview/pull/492) (thanks to [AAkira](https://github.com/AAkira))
- Merge "iOS CookieManager.getCookies - Check that URL has suffix of cookie do…" [#658](https://github.com/pichillilorenzo/flutter_inappwebview/pull/658) (thanks to [arneke](https://github.com/arneke))
- Merge "Add NTLM Auth" [#634](https://github.com/pichillilorenzo/flutter_inappwebview/pull/634) (thanks to [albatrosify](https://github.com/albatrosify))
- Merge "iOS ChromeSafariBrowserManager - Fixing unnecessary casting of rootViewController to FlutterViewController" [#567](https://github.com/pichillilorenzo/flutter_inappwebview/pull/567) (thanks to [gunantosteven](https://github.com/gunantosteven))
- Merge "Fix _channel.invokeMethod name for injectCSSFileFromUrl method" [#645](https://github.com/pichillilorenzo/flutter_inappwebview/pull/645) (thanks to [omralcrt](https://github.com/omralcrt))
- Merge "Add android media intents on wildcard input accept" [#620](https://github.com/pichillilorenzo/flutter_inappwebview/pull/620) (thanks to [cbodin](https://github.com/cbodin))
- Fixed missing properties initialization when using InAppWebViewController.fromInAppBrowser
- Fixed "Issue in Flutter web: 'Unsupported operation: Platform._operatingSystem'" [#507](https://github.com/pichillilorenzo/flutter_inappwebview/issues/507) - Fixed "Issue in Flutter web: 'Unsupported operation: Platform._operatingSystem'" [#507](https://github.com/pichillilorenzo/flutter_inappwebview/issues/507)
- Fixed "window.flutter_inappwebview.callHandler is not a function" [#218](https://github.com/pichillilorenzo/flutter_inappwebview/issues/218) - Fixed "window.flutter_inappwebview.callHandler is not a function" [#218](https://github.com/pichillilorenzo/flutter_inappwebview/issues/218)
- Fixed "Android ContentBlocker - java.lang.NullPointerException ContentBlockerTrigger resource type" [#506](https://github.com/pichillilorenzo/flutter_inappwebview/issues/506) - Fixed "Android ContentBlocker - java.lang.NullPointerException ContentBlockerTrigger resource type" [#506](https://github.com/pichillilorenzo/flutter_inappwebview/issues/506)
- Fixed "Android CookieManager throws error caused by websites that are sending back illegal/invalid cookies." [#476](https://github.com/pichillilorenzo/flutter_inappwebview/issues/476) - Fixed "Android CookieManager throws error caused by websites that are sending back illegal/invalid cookies." [#476](https://github.com/pichillilorenzo/flutter_inappwebview/issues/476)
- Fixed missing `clearHistory` webview method implementation on Android - Fixed missing `clearHistory` webview method implementation on Android
- Fixed iOS crash when using CookieManager getCookies for an URL and the host URL is `null` - Fixed iOS crash when using CookieManager getCookies for an URL and the host URL is `null`
- Fixed "IOS does not support allowUniversalAccessFromFileURLs" [#654](https://github.com/pichillilorenzo/flutter_inappwebview/issues/654)
### BREAKING CHANGES
- Minimum Flutter version required is `1.22.0` and Dart SDK `>=2.12.0-0 <3.0.0`
- Removed `debuggingEnabled` WebView option; on Android you should use now the `AndroidInAppWebViewController.setWebContentsDebuggingEnabled(bool debuggingEnabled)` static method; on iOS, debugging is always enabled
- `allowUniversalAccessFromFileURLs` and `allowFileAccessFromFileURLs` WebView options moved from Android-specific options to cross-platform options.
## 4.0.0+4 ## 4.0.0+4

441
README.md
View File

@ -22,8 +22,8 @@ A Flutter plugin that allows you to add an inline webview, to use an headless we
## Requirements ## Requirements
- Dart sdk: ">=2.7.0 <3.0.0" - Dart sdk: ">=2.12.0-0 <3.0.0"
- Flutter: ">=1.12.13+hotfix.5" - Flutter: ">=1.22.0"
- Android: `minSdkVersion 17` and add support for `androidx` (see [AndroidX Migration](https://flutter.dev/docs/development/androidx-migration) to migrate an existing app) - Android: `minSdkVersion 17` and add support for `androidx` (see [AndroidX Migration](https://flutter.dev/docs/development/androidx-migration) to migrate an existing app)
- iOS: `--ios-language swift`, Xcode version `>= 11` - iOS: `--ios-language swift`, Xcode version `>= 11`
@ -75,7 +75,21 @@ or **Android API 19+** if you enable the `useHybridComposition` Android-specific
- Check the official [Network security configuration - "Opt out of cleartext traffic"](https://developer.android.com/training/articles/security-config#CleartextTrafficPermitted) section. - Check the official [Network security configuration - "Opt out of cleartext traffic"](https://developer.android.com/training/articles/security-config#CleartextTrafficPermitted) section.
- Also, check this StackOverflow issue answer: [Cleartext HTTP traffic not permitted](https://stackoverflow.com/a/50834600/4637638). - Also, check this StackOverflow issue answer: [Cleartext HTTP traffic not permitted](https://stackoverflow.com/a/50834600/4637638).
If you want to use the `ChromeSafariBrowser` on Android 11+ you need to specify your app querying for `android.support.customtabs.action.CustomTabsService` in your `AndroidManifest.xml` you can read more about it here: https://developers.google.com/web/android/custom-tabs/best-practices#applications_targeting_android_11_api_level_30_or_above #### Debugging Android WebViews
On Android, in order to enable/disable debugging WebViews using `chrome://inspect` on Chrome, you should use the `AndroidInAppWebViewController.setWebContentsDebuggingEnabled(bool debuggingEnabled)` static method.
For example, you could call it inside the main function:
```dart
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
if (Platform.isAndroid) {
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
}
runApp(new MyApp());
}
```
### IMPORTANT Note for iOS ### IMPORTANT Note for iOS
@ -139,6 +153,9 @@ Other useful `Info.plist` properties are:
* `NSAllowsLocalNetworking`: A Boolean value indicating whether to allow loading of local resources ([Official wiki](https://developer.apple.com/documentation/bundleresources/information_property_list/nsapptransportsecurity/nsallowslocalnetworking)); * `NSAllowsLocalNetworking`: A Boolean value indicating whether to allow loading of local resources ([Official wiki](https://developer.apple.com/documentation/bundleresources/information_property_list/nsapptransportsecurity/nsallowslocalnetworking));
* `NSAllowsArbitraryLoadsInWebContent`: A Boolean value indicating whether all App Transport Security restrictions are disabled for requests made from web views ([Official wiki](https://developer.apple.com/documentation/bundleresources/information_property_list/nsapptransportsecurity/nsallowsarbitraryloadsinwebcontent)). * `NSAllowsArbitraryLoadsInWebContent`: A Boolean value indicating whether all App Transport Security restrictions are disabled for requests made from web views ([Official wiki](https://developer.apple.com/documentation/bundleresources/information_property_list/nsapptransportsecurity/nsallowsarbitraryloadsinwebcontent)).
#### Debugging iOS WebViews
On iOS, debugging WebViews on Safari through developer tools is always enabled. There isn't a way to enable or disable it.
### How to enable the usage of camera for HTML inputs such as `<input type="file" accept="image/*" capture>` ### How to enable the usage of camera for HTML inputs such as `<input type="file" accept="image/*" capture>`
In order to be able to use camera, for example, for taking images through `<input type="file" accept="image/*" capture>` HTML tag, you need to ask camera permission. In order to be able to use camera, for example, for taking images through `<input type="file" accept="image/*" capture>` HTML tag, you need to ask camera permission.
@ -184,11 +201,11 @@ First, add `flutter_inappwebview` as a [dependency in your pubspec.yaml file](ht
## Usage ## Usage
Classes: Classes:
- [InAppWebView](#inappwebview-class): Flutter Widget for adding an **inline native WebView** integrated into the flutter widget tree. To use `InAppWebView` class on iOS you need to opt-in for the embedded views preview by adding a boolean property to the app's `Info.plist` file, with the key `io.flutter.embedded_views_preview` and the value `YES`. Also, note that on Android it requires **Android API 20+** (see [AndroidView](https://api.flutter.dev/flutter/widgets/AndroidView-class.html)) or **Android API 19+** if you enable the `useHybridComposition` Android-specific option. - [InAppWebView](#inappwebview-class): Flutter Widget for adding an **inline native WebView** integrated into the flutter widget tree. Note that on Android it requires **Android API 20+** (see [AndroidView](https://api.flutter.dev/flutter/widgets/AndroidView-class.html)) or **Android API 19+** if you enable the `useHybridComposition` Android-specific option.
- [ContextMenu](#contextmenu-class): This class represents the WebView context menu. - [ContextMenu](#contextmenu-class): This class represents the WebView context menu.
- [HeadlessInAppWebView](#headlessinappwebview-class): Class that represents a WebView in headless mode. It can be used to run a WebView in background without attaching an `InAppWebView` to the widget tree. - [HeadlessInAppWebView](#headlessinappwebview-class): Class that represents a WebView in headless mode. It can be used to run a WebView in background without attaching an `InAppWebView` to the widget tree.
- [InAppBrowser](#inappbrowser-class): In-App Browser using native WebView. - [InAppBrowser](#inappbrowser-class): In-App Browser using native WebView.
- [ChromeSafariBrowser](#chromesafaribrowser-class): In-App Browser using [Chrome Custom Tabs](https://developer.android.com/reference/android/support/customtabs/package-summary) on Android / [SFSafariViewController](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller) on iOS. - [ChromeSafariBrowser](#chromesafaribrowser-class): In-App Browser using [Chrome Custom Tabs](https://developer.android.com/reference/android/support/customtabs/package-summary) on Android / [SFSafariViewController](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller) on iOS. If you want to use the `ChromeSafariBrowser` on Android 11+ you need to specify your app querying for `android.support.customtabs.action.CustomTabsService` in your `AndroidManifest.xml` (you can read more about it here: https://developers.google.com/web/android/custom-tabs/best-practices#applications_targeting_android_11_api_level_30_or_above).
- [InAppLocalhostServer](#inapplocalhostserver-class): This class allows you to create a simple server on `http://localhost:[port]/`. The default `port` value is `8080`. - [InAppLocalhostServer](#inapplocalhostserver-class): This class allows you to create a simple server on `http://localhost:[port]/`. The default `port` value is `8080`.
- [CookieManager](#cookiemanager-class): This class implements a singleton object (shared instance) which manages the cookies used by WebView instances. **NOTE for iOS**: available from iOS 11.0+. - [CookieManager](#cookiemanager-class): This class implements a singleton object (shared instance) which manages the cookies used by WebView instances. **NOTE for iOS**: available from iOS 11.0+.
- [HttpAuthCredentialDatabase](#httpauthcredentialdatabase-class): This class implements a singleton object (shared instance) which manages the shared HTTP auth credentials cache. - [HttpAuthCredentialDatabase](#httpauthcredentialdatabase-class): This class implements a singleton object (shared instance) which manages the shared HTTP auth credentials cache.
@ -234,20 +251,22 @@ The plugin relies on Flutter's mechanism (in developers preview) for embedding A
Known issues are tagged with the [platform-views](https://github.com/flutter/flutter/labels/a%3A%20platform-views) label in the [Flutter official repo](https://github.com/flutter/flutter). Known issues are tagged with the [platform-views](https://github.com/flutter/flutter/labels/a%3A%20platform-views) label in the [Flutter official repo](https://github.com/flutter/flutter).
Keyboard support within webviews is also experimental. Keyboard support within webviews is also experimental.
To use `InAppWebView` class on iOS you need to opt-in for the embedded views preview by adding a boolean property to the app's `Info.plist` file, with the key `io.flutter.embedded_views_preview` and the value `YES`. Note that on Android it requires **Android API 20+** (see [AndroidView](https://api.flutter.dev/flutter/widgets/AndroidView-class.html))
Also, note that on Android it requires **Android API 20+** (see [AndroidView](https://api.flutter.dev/flutter/widgets/AndroidView-class.html))
or **Android API 19+** if you enable the `useHybridComposition` Android-specific option. or **Android API 19+** if you enable the `useHybridComposition` Android-specific option.
Use `InAppWebViewController` to control the WebView instance. Use `InAppWebViewController` to control the WebView instance.
Example: Example:
```dart ```dart
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart';
Future main() async { Future main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
if (Platform.isAndroid) {
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
}
runApp(new MyApp()); runApp(new MyApp());
} }
@ -258,7 +277,7 @@ class MyApp extends StatefulWidget {
class _MyAppState extends State<MyApp> { class _MyAppState extends State<MyApp> {
InAppWebViewController webView; InAppWebViewController? webView;
String url = ""; String url = "";
double progress = 0; double progress = 0;
@ -280,81 +299,75 @@ class _MyAppState extends State<MyApp> {
title: const Text('InAppWebView Example'), title: const Text('InAppWebView Example'),
), ),
body: Container( body: Container(
child: Column(children: <Widget>[ child: Column(children: <Widget>[
Container( Container(
padding: EdgeInsets.all(20.0), padding: EdgeInsets.all(20.0),
child: Text( child: Text(
"CURRENT URL\n${(url.length > 50) ? url.substring(0, 50) + "..." : url}"), "CURRENT URL\n${(url.length > 50) ? url.substring(0, 50) + "..." : url}"),
), ),
Container( Container(
padding: EdgeInsets.all(10.0), padding: EdgeInsets.all(10.0),
child: progress < 1.0 child: progress < 1.0
? LinearProgressIndicator(value: progress) ? LinearProgressIndicator(value: progress)
: Container()), : Container()),
Expanded( Expanded(
child: Container( child: Container(
margin: const EdgeInsets.all(10.0), margin: const EdgeInsets.all(10.0),
decoration: decoration:
BoxDecoration(border: Border.all(color: Colors.blueAccent)), BoxDecoration(border: Border.all(color: Colors.blueAccent)),
child: InAppWebView( child: InAppWebView(
initialUrl: "https://flutter.dev/", initialUrl: "https://flutter.dev/",
initialHeaders: {}, initialHeaders: {},
initialOptions: InAppWebViewGroupOptions( initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions( crossPlatform: InAppWebViewOptions(
debuggingEnabled: true,
) )
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (controller, url) {
setState(() {
this.url = url ?? '';
});
},
onLoadStop: (controller, url) async {
setState(() {
this.url = url ?? '';
});
},
onProgressChanged: (controller, progress) {
setState(() {
this.progress = progress / 100;
});
},
), ),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (InAppWebViewController controller, String url) {
setState(() {
this.url = url;
});
},
onLoadStop: (InAppWebViewController controller, String url) async {
setState(() {
this.url = url;
});
},
onProgressChanged: (InAppWebViewController controller, int progress) {
setState(() {
this.progress = progress / 100;
});
},
), ),
), ),
), ButtonBar(
ButtonBar( alignment: MainAxisAlignment.center,
alignment: MainAxisAlignment.center, children: <Widget>[
children: <Widget>[ RaisedButton(
RaisedButton( child: Icon(Icons.arrow_back),
child: Icon(Icons.arrow_back), onPressed: () {
onPressed: () { webView?.goBack();
if (webView != null) { },
webView.goBack(); ),
} RaisedButton(
}, child: Icon(Icons.arrow_forward),
), onPressed: () {
RaisedButton( webView?.goForward();
child: Icon(Icons.arrow_forward), },
onPressed: () { ),
if (webView != null) { RaisedButton(
webView.goForward(); child: Icon(Icons.refresh),
} onPressed: () {
}, webView?.reload();
), },
RaisedButton( ),
child: Icon(Icons.refresh), ],
onPressed: () { ),
if (webView != null) { ])),
webView.reload();
}
},
),
],
),
])),
), ),
); );
} }
@ -447,7 +460,7 @@ Methods available:
##### `InAppWebViewController` Android-specific methods ##### `InAppWebViewController` Android-specific methods
Android-specific methods can be called using the `InAppWebViewController.android` attribute. Android-specific methods can be called using the `InAppWebViewController.android` attribute. Static methods can be called using the `AndroidInAppWebViewController` class directly.
* `startSafeBrowsing`: Starts Safe Browsing initialization. * `startSafeBrowsing`: Starts Safe Browsing initialization.
* `clearSslPreferences`: Clears the SSL preferences table stored in response to proceeding with SSL certificate errors. * `clearSslPreferences`: Clears the SSL preferences table stored in response to proceeding with SSL certificate errors.
@ -464,10 +477,11 @@ Android-specific methods can be called using the `InAppWebViewController.android
* `static getSafeBrowsingPrivacyPolicyUrl`: Returns a URL pointing to the privacy policy for Safe Browsing reporting. This value will never be `null`. * `static getSafeBrowsingPrivacyPolicyUrl`: Returns a URL pointing to the privacy policy for Safe Browsing reporting. This value will never be `null`.
* `static setSafeBrowsingWhitelist({@required List<String> hosts})`: Sets the list of hosts (domain names/IP addresses) that are exempt from SafeBrowsing checks. The list is global for all the WebViews. * `static setSafeBrowsingWhitelist({@required List<String> hosts})`: Sets the list of hosts (domain names/IP addresses) that are exempt from SafeBrowsing checks. The list is global for all the WebViews.
* `static getCurrentWebViewPackage`: Gets the current Android WebView package info. * `static getCurrentWebViewPackage`: Gets the current Android WebView package info.
* `static setWebContentsDebuggingEnabled(bool debuggingEnabled)`: Enables debugging of web contents (HTML / CSS / JavaScript) loaded into any WebViews of this application. Debugging is disabled by default.
##### `InAppWebViewController` iOS-specific methods ##### `InAppWebViewController` iOS-specific methods
iOS-specific methods can be called using the `InAppWebViewController.ios` attribute. iOS-specific methods can be called using the `InAppWebViewController.ios` attribute. Static methods can be called using the `IOSInAppWebViewController` class directly.
* `hasOnlySecureContent`: A Boolean value indicating whether all resources on the page have been loaded over securely encrypted connections. * `hasOnlySecureContent`: A Boolean value indicating whether all resources on the page have been loaded over securely encrypted connections.
* `reloadFromOrigin`: Reloads the current page, performing end-to-end revalidation using cache-validating conditionals if possible. * `reloadFromOrigin`: Reloads the current page, performing end-to-end revalidation using cache-validating conditionals if possible.
@ -521,112 +535,111 @@ Instead, on the `onLoadStop` WebView event, you can use `callHandler` directly:
##### `InAppWebView` Cross-platform options ##### `InAppWebView` Cross-platform options
* `useShouldOverrideUrlLoading`: Set to `true` to be able to listen at the `shouldOverrideUrlLoading` event. The default value is `false`. * `allowFileAccessFromFileURLs`: Sets whether JavaScript running in the context of a file scheme URL should be allowed to access content from other file scheme URLs. The default value is `false`.
* `useOnLoadResource`: Set to `true` to be able to listen at the `onLoadResource` event. The default value is `false`. * `allowUniversalAccessFromFileURLs`: Sets whether JavaScript running in the context of a file scheme URL should be allowed to access content from any origin. The default value is `false`.
* `useOnDownloadStart`: Set to `true` to be able to listen at the `onDownloadStart` event. The default value is `false`.
* `useShouldInterceptAjaxRequest`: Set to `true` to be able to listen at the `shouldInterceptAjaxRequest` event. The default value is `false`.
* `useShouldInterceptFetchRequest`: Set to `true` to be able to listen at the `shouldInterceptFetchRequest` event. The default value is `false`.
* `clearCache`: Set to `true` to have all the browser's cache cleared before the new WebView is opened. The default value is `false`.
* `userAgent`: Sets the user-agent for the WebView.
* `applicationNameForUserAgent`: Append to the existing user-agent. Setting userAgent will override this. * `applicationNameForUserAgent`: Append to the existing user-agent. Setting userAgent will override this.
* `javaScriptEnabled`: Set to `true` to enable JavaScript. The default value is `true`. * `cacheEnabled`: Sets whether WebView should use browser caching. The default value is `true`.
* `debuggingEnabled`: Enables debugging of web contents (HTML / CSS / JavaScript) loaded into any WebViews of this application. * `clearCache`: Set to `true` to have all the browser's cache cleared before the new WebView is opened. The default value is `false`.
* `contentBlockers`: List of `ContentBlocker` that are a set of rules used to block content in the browser window.
* `disableContextMenu`: Set to `true` to disable context menu. The default value is `false`.
* `disableHorizontalScroll`: Set to `true` to disable horizontal scroll. The default value is `false`.
* `disableVerticalScroll`: Set to `true` to disable vertical scroll. The default value is `false`.
* `horizontalScrollBarEnabled`: Define whether the horizontal scrollbar should be drawn or not. The default value is `true`.
* `incognito`: Set to `true` to open a browser window with incognito mode. The default value is `false`.
* `javaScriptCanOpenWindowsAutomatically`: Set to `true` to allow JavaScript open windows without user interaction. The default value is `false`. * `javaScriptCanOpenWindowsAutomatically`: Set to `true` to allow JavaScript open windows without user interaction. The default value is `false`.
* `javaScriptEnabled`: Set to `true` to enable JavaScript. The default value is `true`.
* `mediaPlaybackRequiresUserGesture`: Set to `true` to prevent HTML5 audio or video from autoplaying. The default value is `true`. * `mediaPlaybackRequiresUserGesture`: Set to `true` to prevent HTML5 audio or video from autoplaying. The default value is `true`.
* `minimumFontSize`: Sets the minimum font size. The default value is `8` for Android, `0` for iOS. * `minimumFontSize`: Sets the minimum font size. The default value is `8` for Android, `0` for iOS.
* `verticalScrollBarEnabled`: Define whether the vertical scrollbar should be drawn or not. The default value is `true`.
* `horizontalScrollBarEnabled`: Define whether the horizontal scrollbar should be drawn or not. The default value is `true`.
* `resourceCustomSchemes`: List of custom schemes that the WebView must handle. Use the `onLoadResourceCustomScheme` event to intercept resource requests with custom scheme.
* `contentBlockers`: List of `ContentBlocker` that are a set of rules used to block content in the browser window.
* `preferredContentMode`: Sets the content mode that the WebView needs to use when loading and rendering a webpage. The default value is `InAppWebViewUserPreferredContentMode.RECOMMENDED`. * `preferredContentMode`: Sets the content mode that the WebView needs to use when loading and rendering a webpage. The default value is `InAppWebViewUserPreferredContentMode.RECOMMENDED`.
* `incognito`: Set to `true` to open a browser window with incognito mode. The default value is `false`. * `resourceCustomSchemes`: List of custom schemes that the WebView must handle. Use the `onLoadResourceCustomScheme` event to intercept resource requests with custom scheme.
* `cacheEnabled`: Sets whether WebView should use browser caching. The default value is `true`.
* `transparentBackground`: Set to `true` to make the background of the WebView transparent. If your app has a dark theme, this can prevent a white flash on initialization. The default value is `false`.
* `disableVerticalScroll`: Set to `true` to disable vertical scroll. The default value is `false`.
* `disableHorizontalScroll`: Set to `true` to disable horizontal scroll. The default value is `false`.
* `disableContextMenu`: Set to `true` to disable context menu. The default value is `false`.
* `supportZoom`: Set to `false` if the WebView should not support zooming using its on-screen zoom controls and gestures. The default value is `true`. * `supportZoom`: Set to `false` if the WebView should not support zooming using its on-screen zoom controls and gestures. The default value is `true`.
* `transparentBackground`: Set to `true` to make the background of the WebView transparent. If your app has a dark theme, this can prevent a white flash on initialization. The default value is `false`.
* `useOnDownloadStart`: Set to `true` to be able to listen at the `onDownloadStart` event. The default value is `false`.
* `useOnLoadResource`: Set to `true` to be able to listen at the `onLoadResource` event. The default value is `false`.
* `useShouldInterceptAjaxRequest`: Set to `true` to be able to listen at the `shouldInterceptAjaxRequest` event. The default value is `false`.
* `useShouldInterceptFetchRequest`: Set to `true` to be able to listen at the `shouldInterceptFetchRequest` event. The default value is `false`.
* `useShouldOverrideUrlLoading`: Set to `true` to be able to listen at the `shouldOverrideUrlLoading` event. The default value is `false`.
* `userAgent`: Sets the user-agent for the WebView.
* `verticalScrollBarEnabled`: Define whether the vertical scrollbar should be drawn or not. The default value is `true`.
##### `InAppWebView` Android-specific options ##### `InAppWebView` Android-specific options
* `useHybridComposition`: Set to `true` to use Flutter's new Hybrid Composition rendering method, which fixes all issues [here](https://github.com/flutter/flutter/issues/61133). The default value is `false`. Note that this option requires Flutter v1.20+ and should only be used on Android 10+ for release apps, as animations will drop frames on < Android 10 (see [Hybrid-Composition#performance](https://github.com/flutter/flutter/wiki/Hybrid-Composition#performance)).
* `useShouldInterceptRequest`: Set to `true` to be able to listen at the `androidShouldInterceptRequest` event. The default value is `false`.
* `useOnRenderProcessGone`: Set to `true` to be able to listen at the `androidOnRenderProcessGone` event. The default value is `false`.
* `textZoom`: Sets the text zoom of the page in percent. The default value is `100`.
* `clearSessionCache`: Set to `true` to have the session cookie cache cleared before the new window is opened.
* `builtInZoomControls`: Set to `true` if the WebView should use its built-in zoom mechanisms. The default value is `true`.
* `displayZoomControls`: Set to `true` if the WebView should display on-screen zoom controls when using the built-in zoom mechanisms. The default value is `false`.
* `databaseEnabled`: Set to `true` if you want the database storage API is enabled. The default value is `true`.
* `domStorageEnabled`: Set to `true` if you want the DOM storage API is enabled. The default value is `true`.
* `useWideViewPort`: Set to `true` if the WebView should enable support for the "viewport" HTML meta tag or should use a wide viewport.
* `safeBrowsingEnabled`: Sets whether Safe Browsing is enabled. Safe Browsing allows WebView to protect against malware and phishing attacks by verifying the links.
* `mixedContentMode`: Configures the WebView's behavior when a secure origin attempts to load a resource from an insecure origin.
* `allowContentAccess`: Enables or disables content URL access within WebView. Content URL access allows WebView to load content from a content provider installed in the system. The default value is `true`. * `allowContentAccess`: Enables or disables content URL access within WebView. Content URL access allows WebView to load content from a content provider installed in the system. The default value is `true`.
* `allowFileAccess`: Enables or disables file access within WebView. Note that this enables or disables file system access only. * `allowFileAccess`: Enables or disables file access within WebView. Note that this enables or disables file system access only.
* `allowFileAccessFromFileURLs`: Sets whether JavaScript running in the context of a file scheme URL should be allowed to access content from other file scheme URLs.
* `allowUniversalAccessFromFileURLs`: Sets whether JavaScript running in the context of a file scheme URL should be allowed to access content from any origin.
* `appCachePath`: Sets the path to the Application Caches files. In order for the Application Caches API to be enabled, this option must be set a path to which the application can write. * `appCachePath`: Sets the path to the Application Caches files. In order for the Application Caches API to be enabled, this option must be set a path to which the application can write.
* `blockNetworkImage`: Sets whether the WebView should not load image resources from the network (resources accessed via http and https URI schemes). The default value is `false`. * `blockNetworkImage`: Sets whether the WebView should not load image resources from the network (resources accessed via http and https URI schemes). The default value is `false`.
* `blockNetworkLoads`: Sets whether the WebView should not load resources from the network. The default value is `false`. * `blockNetworkLoads`: Sets whether the WebView should not load resources from the network. The default value is `false`.
* `builtInZoomControls`: Set to `true` if the WebView should use its built-in zoom mechanisms. The default value is `true`.
* `cacheMode`: Overrides the way the cache is used. The way the cache is used is based on the navigation type. For a normal page load, the cache is checked and content is re-validated as needed. * `cacheMode`: Overrides the way the cache is used. The way the cache is used is based on the navigation type. For a normal page load, the cache is checked and content is re-validated as needed.
* `clearSessionCache`: Set to `true` to have the session cookie cache cleared before the new window is opened.
* `cursiveFontFamily`: Sets the cursive font family name. The default value is `"cursive"`. * `cursiveFontFamily`: Sets the cursive font family name. The default value is `"cursive"`.
* `databaseEnabled`: Set to `true` if you want the database storage API is enabled. The default value is `true`.
* `defaultFixedFontSize`: Sets the default fixed font size. The default value is `16`. * `defaultFixedFontSize`: Sets the default fixed font size. The default value is `16`.
* `defaultFontSize`: Sets the default font size. The default value is `16`. * `defaultFontSize`: Sets the default font size. The default value is `16`.
* `defaultTextEncodingName`: Sets the default text encoding name to use when decoding html pages. The default value is `"UTF-8"`. * `defaultTextEncodingName`: Sets the default text encoding name to use when decoding html pages. The default value is `"UTF-8"`.
* `disableDefaultErrorPage`: Sets whether the default Android error page should be disabled. The default value is `false`.
* `disabledActionModeMenuItems`: Disables the action mode menu items according to menuItems flag. * `disabledActionModeMenuItems`: Disables the action mode menu items according to menuItems flag.
* `displayZoomControls`: Set to `true` if the WebView should display on-screen zoom controls when using the built-in zoom mechanisms. The default value is `false`.
* `domStorageEnabled`: Set to `true` if you want the DOM storage API is enabled. The default value is `true`.
* `fantasyFontFamily`: Sets the fantasy font family name. The default value is `"fantasy"`. * `fantasyFontFamily`: Sets the fantasy font family name. The default value is `"fantasy"`.
* `fixedFontFamily`: Sets the fixed font family name. The default value is `"monospace"`. * `fixedFontFamily`: Sets the fixed font family name. The default value is `"monospace"`.
* `forceDark`: Set the force dark mode for this WebView. The default value is `AndroidInAppWebViewForceDark.FORCE_DARK_OFF`. * `forceDark`: Set the force dark mode for this WebView. The default value is `AndroidInAppWebViewForceDark.FORCE_DARK_OFF`.
* `geolocationEnabled`: Sets whether Geolocation API is enabled. The default value is `true`. * `geolocationEnabled`: Sets whether Geolocation API is enabled. The default value is `true`.
* `hardwareAcceleration`: Boolean value to enable Hardware Acceleration in the WebView.
* `initialScale`: Sets the initial scale for this WebView. 0 means default. The behavior for the default scale depends on the state of `useWideViewPort` and `loadWithOverviewMode`.
* `layoutAlgorithm`: Sets the underlying layout algorithm. This will cause a re-layout of the WebView. * `layoutAlgorithm`: Sets the underlying layout algorithm. This will cause a re-layout of the WebView.
* `loadWithOverviewMode`: Sets whether the WebView loads pages in overview mode, that is, zooms out the content to fit on screen by width. * `loadWithOverviewMode`: Sets whether the WebView loads pages in overview mode, that is, zooms out the content to fit on screen by width.
* `loadsImagesAutomatically`: Sets whether the WebView should load image resources. Note that this method controls loading of all images, including those embedded using the data URI scheme. * `loadsImagesAutomatically`: Sets whether the WebView should load image resources. Note that this method controls loading of all images, including those embedded using the data URI scheme.
* `minimumLogicalFontSize`: Sets the minimum logical font size. The default is `8`. * `minimumLogicalFontSize`: Sets the minimum logical font size. The default is `8`.
* `initialScale`: Sets the initial scale for this WebView. 0 means default. The behavior for the default scale depends on the state of `useWideViewPort` and `loadWithOverviewMode`. * `mixedContentMode`: Configures the WebView's behavior when a secure origin attempts to load a resource from an insecure origin.
* `needInitialFocus`: Tells the WebView whether it needs to set a node. The default value is `true`. * `needInitialFocus`: Tells the WebView whether it needs to set a node. The default value is `true`.
* `offscreenPreRaster`: Sets whether this WebView should raster tiles when it is offscreen but attached to a window. * `offscreenPreRaster`: Sets whether this WebView should raster tiles when it is offscreen but attached to a window.
* `overScrollMode`: Sets the WebView's over-scroll mode. The default value is `AndroidOverScrollMode.OVER_SCROLL_IF_CONTENT_SCROLLS`.
* `regexToCancelSubFramesLoading`: Regular expression used by `shouldOverrideUrlLoading` event to cancel navigation 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.
* `rendererPriorityPolicy`: Set the renderer priority policy for this WebView.
* `safeBrowsingEnabled`: Sets whether Safe Browsing is enabled. Safe Browsing allows WebView to protect against malware and phishing attacks by verifying the links.
* `sansSerifFontFamily`: Sets the sans-serif font family name. The default value is `"sans-serif"`. * `sansSerifFontFamily`: Sets the sans-serif font family name. The default value is `"sans-serif"`.
* `saveFormData`: Sets whether the WebView should save form data. In Android O, the platform has implemented a fully functional Autofill feature to store form data.
* `scrollBarDefaultDelayBeforeFade`: Defines the delay in milliseconds that a scrollbar waits before fade out.
* `scrollBarFadeDuration`: Define the scrollbar fade duration in milliseconds.
* `scrollBarStyle`: Specify the style of the scrollbars. The scrollbars can be overlaid or inset. The default value is `AndroidScrollBarStyle.SCROLLBARS_INSIDE_OVERLAY`.
* `scrollbarFadingEnabled`: Define whether scrollbars will fade when the view is not scrolling. The default value is `true`.
* `serifFontFamily`: Sets the serif font family name. The default value is `"sans-serif"`. * `serifFontFamily`: Sets the serif font family name. The default value is `"sans-serif"`.
* `standardFontFamily`: Sets the standard font family name. The default value is `"sans-serif"`. * `standardFontFamily`: Sets the standard font family name. The default value is `"sans-serif"`.
* `saveFormData`: Sets whether the WebView should save form data. In Android O, the platform has implemented a fully functional Autofill feature to store form data.
* `thirdPartyCookiesEnabled`: Boolean value to enable third party cookies in the WebView.
* `hardwareAcceleration`: Boolean value to enable Hardware Acceleration in the WebView.
* `supportMultipleWindows`: Sets whether the WebView whether supports multiple windows. * `supportMultipleWindows`: Sets whether the WebView whether supports multiple windows.
* `regexToCancelSubFramesLoading`: Regular expression used by `shouldOverrideUrlLoading` event to cancel navigation 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. * `textZoom`: Sets the text zoom of the page in percent. The default value is `100`.
* `overScrollMode`: Sets the WebView's over-scroll mode. The default value is `AndroidOverScrollMode.OVER_SCROLL_IF_CONTENT_SCROLLS`. * `thirdPartyCookiesEnabled`: Boolean value to enable third party cookies in the WebView.
* `scrollBarStyle`: Specify the style of the scrollbars. The scrollbars can be overlaid or inset. The default value is `AndroidScrollBarStyle.SCROLLBARS_INSIDE_OVERLAY`. * `useHybridComposition`: Set to `true` to use Flutter's new Hybrid Composition rendering method, which fixes all issues [here](https://github.com/flutter/flutter/issues/61133). The default value is `false`. Note that this option requires Flutter v1.20+ and should only be used on Android 10+ for release apps, as animations will drop frames on < Android 10 (see [Hybrid-Composition#performance](https://github.com/flutter/flutter/wiki/Hybrid-Composition#performance)).
* `useOnRenderProcessGone`: Set to `true` to be able to listen at the `androidOnRenderProcessGone` event. The default value is `false`.
* `useShouldInterceptRequest`: Set to `true` to be able to listen at the `androidShouldInterceptRequest` event. The default value is `false`.
* `useWideViewPort`: Set to `true` if the WebView should enable support for the "viewport" HTML meta tag or should use a wide viewport.
* `verticalScrollbarPosition`: Set the position of the vertical scroll bar. The default value is `AndroidVerticalScrollbarPosition.SCROLLBAR_POSITION_DEFAULT`. * `verticalScrollbarPosition`: Set the position of the vertical scroll bar. The default value is `AndroidVerticalScrollbarPosition.SCROLLBAR_POSITION_DEFAULT`.
* `scrollBarDefaultDelayBeforeFade`: Defines the delay in milliseconds that a scrollbar waits before fade out.
* `scrollbarFadingEnabled`: Define whether scrollbars will fade when the view is not scrolling. The default value is `true`.
* `scrollBarFadeDuration`: Define the scrollbar fade duration in milliseconds.
* `rendererPriorityPolicy`: Set the renderer priority policy for this WebView.
* `disableDefaultErrorPage`: Sets whether the default Android error page should be disabled. The default value is `false`.
##### `InAppWebView` iOS-specific options ##### `InAppWebView` iOS-specific options
* `disallowOverScroll`: Set to `true` to disable the bouncing of the WebView when the scrolling has reached an edge of the content. The default value is `false`. * `accessibilityIgnoresInvertColors`: A Boolean value indicating whether the view ignores an accessibility request to invert its colors. The default value is `false`.
* `enableViewportScale`: Set to `true` to allow a viewport meta tag to either disable or restrict the range of user scaling. The default value is `false`.
* `suppressesIncrementalRendering`: Set to `true` if you want the WebView suppresses content rendering until it is fully loaded into memory. The default value is `false`.
* `allowsAirPlayForMediaPlayback`: Set to `true` to allow AirPlay. The default value is `true`. * `allowsAirPlayForMediaPlayback`: Set to `true` to allow AirPlay. The default value is `true`.
* `allowsBackForwardNavigationGestures`: Set to `true` to allow the horizontal swipe gestures trigger back-forward list navigations. The default value is `true`. * `allowsBackForwardNavigationGestures`: Set to `true` to allow the horizontal swipe gestures trigger back-forward list navigations. The default value is `true`.
* `allowsLinkPreview`: Set to `true` to allow that pressing on a link displays a preview of the destination for the link. The default value is `true`.
* `ignoresViewportScaleLimits`: Set to `true` if you want that the WebView should always allow scaling of the webpage, regardless of the author's intent.
* `allowsInlineMediaPlayback`: Set to `true` to allow HTML5 media playback to appear inline within the screen layout, using browser-supplied controls rather than native controls. * `allowsInlineMediaPlayback`: Set to `true` to allow HTML5 media playback to appear inline within the screen layout, using browser-supplied controls rather than native controls.
* `allowsLinkPreview`: Set to `true` to allow that pressing on a link displays a preview of the destination for the link. The default value is `true`.
* `allowsPictureInPictureMediaPlayback`: Set to `true` to allow HTML5 videos play picture-in-picture. The default value is `true`. * `allowsPictureInPictureMediaPlayback`: Set to `true` to allow HTML5 videos play picture-in-picture. The default value is `true`.
* `isFraudulentWebsiteWarningEnabled`: A Boolean value indicating whether warnings should be shown for suspected fraudulent content such as phishing or malware.
* `selectionGranularity`: The level of granularity with which the user can interactively select content in the web view.
* `dataDetectorTypes`: Specifying a dataDetectoryTypes value adds interactivity to web content that matches the value.
* `sharedCookiesEnabled`: Set `true` if shared cookies from `HTTPCookieStorage.shared` should used for every load request in the WebView.
* `automaticallyAdjustsScrollIndicatorInsets`: Configures whether the scroll indicator insets are automatically adjusted by the system. The default value is `false`.
* `accessibilityIgnoresInvertColors`: A Boolean value indicating whether the view ignores an accessibility request to invert its colors. The default value is `false`.
* `decelerationRate`: A `IOSUIScrollViewDecelerationRate` value that determines the rate of deceleration after the user lifts their finger. The default value is `IOSUIScrollViewDecelerationRate.NORMAL`.
* `alwaysBounceVertical`: A Boolean value that determines whether bouncing always occurs when vertical scrolling reaches the end of the content. The default value is `false`.
* `alwaysBounceHorizontal`: A Boolean value that determines whether bouncing always occurs when horizontal scrolling reaches the end of the content view. The default value is `false`. * `alwaysBounceHorizontal`: A Boolean value that determines whether bouncing always occurs when horizontal scrolling reaches the end of the content view. The default value is `false`.
* `scrollsToTop`: A Boolean value that controls whether the scroll-to-top gesture is enabled. The default value is `true`. * `alwaysBounceVertical`: A Boolean value that determines whether bouncing always occurs when vertical scrolling reaches the end of the content. The default value is `false`.
* `automaticallyAdjustsScrollIndicatorInsets`: Configures whether the scroll indicator insets are automatically adjusted by the system. The default value is `false`.
* `contentInsetAdjustmentBehavior`: Configures how safe area insets are added to the adjusted content inset. The default value is `IOSUIScrollViewContentInsetAdjustmentBehavior.NEVER`.
* `dataDetectorTypes`: Specifying a dataDetectoryTypes value adds interactivity to web content that matches the value.
* `decelerationRate`: A `IOSUIScrollViewDecelerationRate` value that determines the rate of deceleration after the user lifts their finger. The default value is `IOSUIScrollViewDecelerationRate.NORMAL`.
* `disallowOverScroll`: Set to `true` to disable the bouncing of the WebView when the scrolling has reached an edge of the content. The default value is `false`.
* `enableViewportScale`: Set to `true` to allow a viewport meta tag to either disable or restrict the range of user scaling. The default value is `false`.
* `ignoresViewportScaleLimits`: Set to `true` if you want that the WebView should always allow scaling of the webpage, regardless of the author's intent.
* `isFraudulentWebsiteWarningEnabled`: A Boolean value indicating whether warnings should be shown for suspected fraudulent content such as phishing or malware.
* `isPagingEnabled`: A Boolean value that determines whether paging is enabled for the scroll view. The default value is `false`. * `isPagingEnabled`: A Boolean value that determines whether paging is enabled for the scroll view. The default value is `false`.
* `maximumZoomScale`: A floating-point value that specifies the maximum scale factor that can be applied to the scroll view's content. The default value is `1.0`. * `maximumZoomScale`: A floating-point value that specifies the maximum scale factor that can be applied to the scroll view's content. The default value is `1.0`.
* `minimumZoomScale`: A floating-point value that specifies the minimum scale factor that can be applied to the scroll view's content. The default value is `1.0`. * `minimumZoomScale`: A floating-point value that specifies the minimum scale factor that can be applied to the scroll view's content. The default value is `1.0`.
* `contentInsetAdjustmentBehavior`: Configures how safe area insets are added to the adjusted content inset. The default value is `IOSUIScrollViewContentInsetAdjustmentBehavior.NEVER`. * `scrollsToTop`: A Boolean value that controls whether the scroll-to-top gesture is enabled. The default value is `true`.
* `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`.
#### `InAppWebView` Events #### `InAppWebView` Events
@ -701,6 +714,9 @@ import 'package:flutter_inappwebview/flutter_inappwebview.dart';
Future main() async { Future main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
if (Platform.isAndroid) {
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
}
runApp(new MyApp()); runApp(new MyApp());
} }
@ -711,8 +727,8 @@ class MyApp extends StatefulWidget {
class _MyAppState extends State<MyApp> { class _MyAppState extends State<MyApp> {
InAppWebViewController webView; InAppWebViewController? webView;
ContextMenu contextMenu; ContextMenu? contextMenu;
String url = ""; String url = "";
double progress = 0; double progress = 0;
@ -729,7 +745,7 @@ class _MyAppState extends State<MyApp> {
onCreateContextMenu: (hitTestResult) async { onCreateContextMenu: (hitTestResult) async {
print("onCreateContextMenu"); print("onCreateContextMenu");
print(hitTestResult.extra); print(hitTestResult.extra);
print(await webView.getSelectedText()); print(await webView?.getSelectedText());
}, },
onHideContextMenu: () { onHideContextMenu: () {
print("onHideContextMenu"); print("onHideContextMenu");
@ -777,23 +793,23 @@ class _MyAppState extends State<MyApp> {
initialHeaders: {}, initialHeaders: {},
initialOptions: InAppWebViewGroupOptions( initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions( crossPlatform: InAppWebViewOptions(
debuggingEnabled: true,
) )
), ),
onWebViewCreated: (InAppWebViewController controller) { onWebViewCreated: (InAppWebViewController controller) {
webView = controller; webView = controller;
}, },
onLoadStart: (InAppWebViewController controller, String url) { onLoadStart: (controller, url) {
setState(() { setState(() {
this.url = url; this.url = url ?? '';
}); });
}, },
onLoadStop: (InAppWebViewController controller, String url) async { onLoadStop: (controller, url) async {
setState(() { setState(() {
this.url = url; this.url = url ?? '';
}); });
}, },
onProgressChanged: (InAppWebViewController controller, int progress) { onProgressChanged: (controller, progress) {
setState(() { setState(() {
this.progress = progress / 100; this.progress = progress / 100;
}); });
@ -807,25 +823,19 @@ class _MyAppState extends State<MyApp> {
RaisedButton( RaisedButton(
child: Icon(Icons.arrow_back), child: Icon(Icons.arrow_back),
onPressed: () { onPressed: () {
if (webView != null) { webView?.goBack();
webView.goBack();
}
}, },
), ),
RaisedButton( RaisedButton(
child: Icon(Icons.arrow_forward), child: Icon(Icons.arrow_forward),
onPressed: () { onPressed: () {
if (webView != null) { webView?.goForward();
webView.goForward();
}
}, },
), ),
RaisedButton( RaisedButton(
child: Icon(Icons.refresh), child: Icon(Icons.refresh),
onPressed: () { onPressed: () {
if (webView != null) { webView?.reload();
webView.reload();
}
}, },
), ),
], ],
@ -858,12 +868,16 @@ As `InAppWebView`, it has the same options and events. Use `InAppWebViewControll
Example: Example:
```dart ```dart
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart';
Future main() async { Future main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
if (Platform.isAndroid) {
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
}
runApp(new MyApp()); runApp(new MyApp());
} }
@ -874,7 +888,7 @@ class MyApp extends StatefulWidget {
class _MyAppState extends State<MyApp> { class _MyAppState extends State<MyApp> {
HeadlessInAppWebView headlessWebView; HeadlessInAppWebView? headlessWebView;
String url = ""; String url = "";
@override @override
@ -885,7 +899,7 @@ class _MyAppState extends State<MyApp> {
initialUrl: "https://flutter.dev/", initialUrl: "https://flutter.dev/",
initialOptions: InAppWebViewGroupOptions( initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions( crossPlatform: InAppWebViewOptions(
debuggingEnabled: true,
), ),
), ),
onWebViewCreated: (controller) { onWebViewCreated: (controller) {
@ -897,19 +911,19 @@ class _MyAppState extends State<MyApp> {
onLoadStart: (controller, url) async { onLoadStart: (controller, url) async {
print("onLoadStart $url"); print("onLoadStart $url");
setState(() { setState(() {
this.url = url; this.url = url ?? '';
}); });
}, },
onLoadStop: (controller, url) async { onLoadStop: (controller, url) async {
print("onLoadStop $url"); print("onLoadStop $url");
setState(() { setState(() {
this.url = url; this.url = url ?? '';
}); });
}, },
onUpdateVisitedHistory: (InAppWebViewController controller, String url, bool androidIsReload) { onUpdateVisitedHistory: (controller, url, androidIsReload) {
print("onUpdateVisitedHistory $url"); print("onUpdateVisitedHistory $url");
setState(() { setState(() {
this.url = url; this.url = url ?? '';
}); });
}, },
); );
@ -918,7 +932,7 @@ class _MyAppState extends State<MyApp> {
@override @override
void dispose() { void dispose() {
super.dispose(); super.dispose();
headlessWebView.dispose(); headlessWebView?.dispose();
} }
@override @override
@ -938,8 +952,8 @@ class _MyAppState extends State<MyApp> {
Center( Center(
child: RaisedButton( child: RaisedButton(
onPressed: () async { onPressed: () async {
await headlessWebView.dispose(); await headlessWebView?.dispose();
await headlessWebView.run(); await headlessWebView?.run();
}, },
child: Text("Run HeadlessInAppWebView")), child: Text("Run HeadlessInAppWebView")),
), ),
@ -947,7 +961,7 @@ class _MyAppState extends State<MyApp> {
child: RaisedButton( child: RaisedButton(
onPressed: () async { onPressed: () async {
try { try {
await headlessWebView.webViewController.evaluateJavascript(source: """console.log('Here is the message!');"""); await headlessWebView?.webViewController.evaluateJavascript(source: """console.log('Here is the message!');""");
} on MissingPluginException catch(e) { } on MissingPluginException catch(e) {
print("HeadlessInAppWebView is not running. Click on \"Run HeadlessInAppWebView\"!"); print("HeadlessInAppWebView is not running. Click on \"Run HeadlessInAppWebView\"!");
} }
@ -957,7 +971,7 @@ class _MyAppState extends State<MyApp> {
Center( Center(
child: RaisedButton( child: RaisedButton(
onPressed: () { onPressed: () {
headlessWebView.dispose(); headlessWebView?.dispose();
}, },
child: Text("Dispose HeadlessInAppWebView")), child: Text("Dispose HeadlessInAppWebView")),
) )
@ -977,6 +991,8 @@ In-App Browser using native WebView.
Create a Class that extends the `InAppBrowser` Class in order to override the callbacks to manage the browser events. Create a Class that extends the `InAppBrowser` Class in order to override the callbacks to manage the browser events.
Example: Example:
```dart ```dart
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart';
@ -987,22 +1003,22 @@ class MyInAppBrowser extends InAppBrowser {
} }
@override @override
Future onLoadStart(String url) async { Future onLoadStart(url) async {
print("\n\nStarted $url\n\n"); print("\n\nStarted $url\n\n");
} }
@override @override
Future onLoadStop(String url) async { Future onLoadStop(url) async {
print("\n\nStopped $url\n\n"); print("\n\nStopped $url\n\n");
} }
@override @override
void onLoadError(String url, int code, String message) { void onLoadError(url, code, message) {
print("Can't load $url.. Error: $message"); print("Can't load $url.. Error: $message");
} }
@override @override
void onProgressChanged(int progress) { void onProgressChanged(progress) {
print("Progress: $progress"); print("Progress: $progress");
} }
@ -1024,7 +1040,7 @@ class MyInAppBrowser extends InAppBrowser {
"ms ---> duration: " + "ms ---> duration: " +
response.duration.toString() + response.duration.toString() +
"ms " + "ms " +
response.url); (response.url ?? ''));
} }
@override @override
@ -1032,13 +1048,16 @@ class MyInAppBrowser extends InAppBrowser {
print(""" print("""
console output: console output:
message: ${consoleMessage.message} message: ${consoleMessage.message}
messageLevel: ${consoleMessage.messageLevel.toValue()} messageLevel: ${consoleMessage.messageLevel?.toValue()}
"""); """);
} }
} }
void main() { Future main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
if (Platform.isAndroid) {
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
}
runApp( runApp(
new MyApp(), new MyApp(),
); );
@ -1046,7 +1065,7 @@ void main() {
class MyApp extends StatefulWidget { class MyApp extends StatefulWidget {
final MyInAppBrowser browser = new MyInAppBrowser(); final MyInAppBrowser browser = new MyInAppBrowser();
@override @override
_MyAppState createState() => new _MyAppState(); _MyAppState createState() => new _MyAppState();
} }
@ -1111,27 +1130,27 @@ Specific options of the `InAppBrowser` class are:
##### `InAppBrowser` Cross-platform options ##### `InAppBrowser` Cross-platform options
* `hidden`: Set to `true` to create the browser and load the page, but not show it. Omit or set to `false` to have the browser open and load normally. The default value is `false`. * `hidden`: Set to `true` to create the browser and load the page, but not show it. Omit or set to `false` to have the browser open and load normally. The default value is `false`.
* `toolbarTop`: Set to `false` to hide the toolbar at the top of the WebView. The default value is `true`.
* `toolbarTopBackgroundColor`: Set the custom background color of the toolbar at the top.
* `hideUrlBar`: Set to `true` to hide the url bar on the toolbar at the top. The default value is `false`. * `hideUrlBar`: Set to `true` to hide the url bar on the toolbar at the top. The default value is `false`.
* `toolbarTopBackgroundColor`: Set the custom background color of the toolbar at the top.
* `toolbarTop`: Set to `false` to hide the toolbar at the top of the WebView. The default value is `true`.
##### `InAppBrowser` Android-specific options ##### `InAppBrowser` Android-specific options
* `hideTitleBar`: Set to `true` if you want the title should be displayed. The default value is `false`.
* `toolbarTopFixedTitle`: Set the action bar's title.
* `closeOnCannotGoBack`: Set to `false` to not close the InAppBrowser when the user click on the back button and the WebView cannot go back to the history. The default value is `true`. * `closeOnCannotGoBack`: Set to `false` to not close the InAppBrowser when the user click on the back button and the WebView cannot go back to the history. The default value is `true`.
* `hideTitleBar`: Set to `true` if you want the title should be displayed. The default value is `false`.
* `progressBar`: Set to `false` to hide the progress bar at the bottom of the toolbar at the top. The default value is `true`. * `progressBar`: Set to `false` to hide the progress bar at the bottom of the toolbar at the top. The default value is `true`.
* `toolbarTopFixedTitle`: Set the action bar's title.
##### `InAppBrowser` iOS-specific options ##### `InAppBrowser` iOS-specific options
* `toolbarBottom`: Set to `false` to hide the toolbar at the bottom of the WebView. The default value is `true`.
* `toolbarBottomBackgroundColor`: Set the custom background color of the toolbar at the bottom.
* `toolbarBottomTranslucent`: Set to `true` to set the toolbar at the bottom translucent. The default value is `true`.
* `closeButtonCaption`: Set the custom text for the close button. * `closeButtonCaption`: Set the custom text for the close button.
* `closeButtonColor`: Set the custom color for the close button. * `closeButtonColor`: Set the custom color for the close button.
* `presentationStyle`: Set the custom modal presentation style when presenting the WebView. The default value is `IOSUIModalPresentationStyle.FULL_SCREEN`. * `presentationStyle`: Set the custom modal presentation style when presenting the WebView. The default value is `IOSUIModalPresentationStyle.FULL_SCREEN`.
* `transitionStyle`: Set to the custom transition style when presenting the WebView. The default value is `IOSUIModalTransitionStyle.COVER_VERTICAL`.
* `spinner`: Set to `false` to hide the spinner when the WebView is loading a page. The default value is `true`. * `spinner`: Set to `false` to hide the spinner when the WebView is loading a page. The default value is `true`.
* `toolbarBottomBackgroundColor`: Set the custom background color of the toolbar at the bottom.
* `toolbarBottomTranslucent`: Set to `true` to set the toolbar at the bottom translucent. The default value is `true`.
* `toolbarBottom`: Set to `false` to hide the toolbar at the bottom of the WebView. The default value is `true`.
* `transitionStyle`: Set to the custom transition style when presenting the WebView. The default value is `IOSUIModalTransitionStyle.COVER_VERTICAL`.
#### `InAppBrowser` Events #### `InAppBrowser` Events
@ -1145,27 +1164,31 @@ Specific events of the `InAppBrowser` class are:
[Chrome Custom Tabs](https://developer.android.com/reference/android/support/customtabs/package-summary) on Android / [SFSafariViewController](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller) on iOS. [Chrome Custom Tabs](https://developer.android.com/reference/android/support/customtabs/package-summary) on Android / [SFSafariViewController](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller) on iOS.
If you want to use the `ChromeSafariBrowser` on Android 11+ you need to specify your app querying for `android.support.customtabs.action.CustomTabsService` in your `AndroidManifest.xml` (you can read more about it here: https://developers.google.com/web/android/custom-tabs/best-practices#applications_targeting_android_11_api_level_30_or_above).
You can initialize the `ChromeSafariBrowser` instance with an `InAppBrowser` fallback instance. You can initialize the `ChromeSafariBrowser` instance with an `InAppBrowser` fallback instance.
Create a Class that extends the `ChromeSafariBrowser` Class in order to override the callbacks to manage the browser events. Example: Create a Class that extends the `ChromeSafariBrowser` Class in order to override the callbacks to manage the browser events. Example:
```dart ```dart
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart';
class MyInAppBrowser extends InAppBrowser { class MyInAppBrowser extends InAppBrowser {
@override @override
Future onLoadStart(String url) async { Future onLoadStart(url) async {
print("\n\nStarted $url\n\n"); print("\n\nStarted $url\n\n");
} }
@override @override
Future onLoadStop(String url) async { Future onLoadStop(url) async {
print("\n\nStopped $url\n\n"); print("\n\nStopped $url\n\n");
} }
@override @override
void onLoadError(String url, int code, String message) { void onLoadError(url, code, message) {
print("\n\nCan't load $url.. Error: $message\n\n"); print("\n\nCan't load $url.. Error: $message\n\n");
} }
@ -1196,11 +1219,12 @@ class MyChromeSafariBrowser extends ChromeSafariBrowser {
} }
} }
void main() { Future main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
runApp( if (Platform.isAndroid) {
new MyApp(), await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
); }
runApp(new MyApp());
} }
class MyApp extends StatefulWidget { class MyApp extends StatefulWidget {
@ -1262,11 +1286,11 @@ Screenshots:
#### `ChromeSafariBrowser` Methods #### `ChromeSafariBrowser` Methods
* `open({@required String url, ChromeSafariBrowserClassOptions options, Map<String, String> headersFallback = const {}, InAppBrowserClassOptions optionsFallback})`: Opens an `url` in a new `ChromeSafariBrowser` instance.
* `isOpened`: Returns `true` if the `ChromeSafariBrowser` instance is opened, otherwise `false`.
* `close`: Closes the `ChromeSafariBrowser` instance.
* `addMenuItem`: Adds a `ChromeSafariBrowserMenuItem` to the menu. * `addMenuItem`: Adds a `ChromeSafariBrowserMenuItem` to the menu.
* `addMenuItems`: Adds a list of `ChromeSafariBrowserMenuItem` to the menu. * `addMenuItems`: Adds a list of `ChromeSafariBrowserMenuItem` to the menu.
* `close`: Closes the `ChromeSafariBrowser` instance.
* `isOpened`: Returns `true` if the `ChromeSafariBrowser` instance is opened, otherwise `false`.
* `open({@required String url, ChromeSafariBrowserClassOptions options, Map<String, String> headersFallback = const {}, InAppBrowserClassOptions optionsFallback})`: Opens an `url` in a new `ChromeSafariBrowser` instance.
* `static isAvailable`: On Android, returns `true` if Chrome Custom Tabs is available. On iOS, returns `true` if SFSafariViewController is available. Otherwise returns `false`. * `static isAvailable`: On Android, returns `true` if Chrome Custom Tabs is available. On iOS, returns `true` if SFSafariViewController is available. Otherwise returns `false`.
#### `ChromeSafariBrowser` options #### `ChromeSafariBrowser` options
@ -1274,18 +1298,18 @@ Screenshots:
##### `ChromeSafariBrowser` Android-specific options ##### `ChromeSafariBrowser` Android-specific options
* `addDefaultShareMenuItem`: Set to `false` if you don't want the default share item to the menu. The default value is `true`. * `addDefaultShareMenuItem`: Set to `false` if you don't want the default share item to the menu. The default value is `true`.
* `showTitle`: Set to `false` if the title shouldn't be shown in the custom tab. The default value is `true`.
* `toolbarBackgroundColor`: Set the custom background color of the toolbar.
* `enableUrlBarHiding`: Set to `true` to enable the url bar to hide as the user scrolls down on the page. The default value is `false`. * `enableUrlBarHiding`: Set to `true` to enable the url bar to hide as the user scrolls down on the page. The default value is `false`.
* `instantAppsEnabled`: Set to `true` to enable Instant Apps. The default value is `false`. * `instantAppsEnabled`: Set to `true` to enable Instant Apps. The default value is `false`.
* `packageName`: Set the name of the application package to handle the intent (for example `com.android.chrome`), or null to allow any application package.
* `keepAliveEnabled`: Set to `true` to enable Keep Alive. The default value is `false`. * `keepAliveEnabled`: Set to `true` to enable Keep Alive. The default value is `false`.
* `packageName`: Set the name of the application package to handle the intent (for example `com.android.chrome`), or null to allow any application package.
* `showTitle`: Set to `false` if the title shouldn't be shown in the custom tab. The default value is `true`.
* `toolbarBackgroundColor`: Set the custom background color of the toolbar.
##### `ChromeSafariBrowser` iOS-specific options ##### `ChromeSafariBrowser` iOS-specific options
* `entersReaderIfAvailable`: Set to `true` if Reader mode should be entered automatically when it is available for the webpage. The default value is `false`.
* `barCollapsingEnabled`: Set to `true` to enable bar collapsing. The default value is `false`. * `barCollapsingEnabled`: Set to `true` to enable bar collapsing. The default value is `false`.
* `dismissButtonStyle`: Set the custom style for the dismiss button. The default value is `IOSSafariDismissButtonStyle.DONE`. * `dismissButtonStyle`: Set the custom style for the dismiss button. The default value is `IOSSafariDismissButtonStyle.DONE`.
* `entersReaderIfAvailable`: Set to `true` if Reader mode should be entered automatically when it is available for the webpage. The default value is `false`.
* `preferredBarTintColor`: Set the custom background color of the navigation bar and the toolbar. * `preferredBarTintColor`: Set the custom background color of the navigation bar and the toolbar.
* `preferredControlTintColor`: Set the custom color of the control buttons on the navigation bar and the toolbar. * `preferredControlTintColor`: Set the custom color of the control buttons on the navigation bar and the toolbar.
* `presentationStyle`: Set the custom modal presentation style when presenting the WebView. The default value is `IOSUIModalPresentationStyle.FULL_SCREEN`. * `presentationStyle`: Set the custom modal presentation style when presenting the WebView. The default value is `IOSUIModalPresentationStyle.FULL_SCREEN`.
@ -1310,6 +1334,9 @@ InAppLocalhostServer localhostServer = new InAppLocalhostServer();
Future main() async { Future main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await localhostServer.start(); await localhostServer.start();
if (Platform.isAndroid) {
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
}
runApp(new MyApp()); runApp(new MyApp());
} }
@ -1330,17 +1357,17 @@ Future main() async {
initialUrl: "http://localhost:8080/assets/index.html", initialUrl: "http://localhost:8080/assets/index.html",
initialHeaders: {}, initialHeaders: {},
initialOptions: InAppWebViewGroupOptions( initialOptions: InAppWebViewGroupOptions(
inAppWebViewOptions: InAppWebViewOptions( crossPlatform: InAppWebViewOptions(
debuggingEnabled: true,
) )
), ),
onWebViewCreated: (InAppWebViewController controller) { onWebViewCreated: (controller) {
}, },
onLoadStart: (InAppWebViewController controller, String url) { onLoadStart: (controller, url) {
}, },
onLoadStop: (InAppWebViewController controller, String url) { onLoadStop: (controller, url) {
}, },
), ),

View File

@ -109,31 +109,33 @@ public class ContentBlockerHandler {
latch.await(); latch.await();
} }
if (!trigger.loadType.isEmpty()) { if (webViewUrl[0] != null) {
URI cUrl = new URI(webViewUrl[0]); if (!trigger.loadType.isEmpty()) {
String cHost = cUrl.getHost(); URI cUrl = new URI(webViewUrl[0]);
int cPort = cUrl.getPort(); String cHost = cUrl.getHost();
String cScheme = cUrl.getScheme(); int cPort = cUrl.getPort();
String cScheme = cUrl.getScheme();
if ( (trigger.loadType.contains("first-party") && cHost != null && !(cScheme.equals(scheme) && cHost.equals(host) && cPort == port)) || if ( (trigger.loadType.contains("first-party") && cHost != null && !(cScheme.equals(scheme) && cHost.equals(host) && cPort == port)) ||
(trigger.loadType.contains("third-party") && cHost != null && cHost.equals(host)) ) (trigger.loadType.contains("third-party") && cHost != null && cHost.equals(host)) )
return null;
}
if (!trigger.ifTopUrl.isEmpty()) {
boolean matchFound = false;
for (String topUrl : trigger.ifTopUrl) {
if (webViewUrl[0].startsWith(topUrl)) {
matchFound = true;
break;
}
}
if (!matchFound)
return null;
}
if (!trigger.unlessTopUrl.isEmpty()) {
for (String topUrl : trigger.unlessTopUrl)
if (webViewUrl[0].startsWith(topUrl))
return null; return null;
}
if (!trigger.ifTopUrl.isEmpty()) {
boolean matchFound = false;
for (String topUrl : trigger.ifTopUrl) {
if (webViewUrl[0].startsWith(topUrl)) {
matchFound = true;
break;
}
}
if (!matchFound)
return null;
}
if (!trigger.unlessTopUrl.isEmpty()) {
for (String topUrl : trigger.unlessTopUrl)
if (webViewUrl[0].startsWith(topUrl))
return null;
}
} }
switch (action.type) { switch (action.type) {

View File

@ -67,28 +67,9 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
"- See the official wiki here: https://github.com/flutter/flutter/wiki/Upgrading-pre-1.12-Android-projects\n\n\n"); "- See the official wiki here: https://github.com/flutter/flutter/wiki/Upgrading-pre-1.12-Android-projects\n\n\n");
} }
// MutableContextWrapper mMutableContext = new MutableContextWrapper(Shared.activity); webView = new InAppWebView(context, this, id, windowId, options, contextMenu, containerView);
// webView = new InAppWebView(mMutableContext, this, id, options, contextMenu, containerView);
// displayListenerProxy.onPostWebViewInitialization(displayManager);
// mMutableContext.setBaseContext(context);
webView = new InAppWebView(Shared.activity, this, id, windowId, options, contextMenu, containerView);
displayListenerProxy.onPostWebViewInitialization(displayManager); displayListenerProxy.onPostWebViewInitialization(displayManager);
// fix https://github.com/pichillilorenzo/flutter_inappwebview/issues/182
try {
Class superClass = webView.getClass().getSuperclass();
while(!superClass.getName().equals("android.view.View")) {
superClass = superClass.getSuperclass();
}
Field mContext = superClass.getDeclaredField("mContext");
mContext.setAccessible(true);
mContext.set(webView, context);
} catch (Exception e) {
e.printStackTrace();
Log.e(LOG_TAG, "Cannot find mContext for this WebView");
}
webView.prepare(); webView.prepare();
if (windowId != null) { if (windowId != null) {

View File

@ -677,9 +677,6 @@ final public class InAppWebView extends InputAwareWebView {
WebSettings settings = getSettings(); WebSettings settings = getSettings();
settings.setJavaScriptEnabled(options.javaScriptEnabled); settings.setJavaScriptEnabled(options.javaScriptEnabled);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
setWebContentsDebuggingEnabled(options.debuggingEnabled);
}
settings.setJavaScriptCanOpenWindowsAutomatically(options.javaScriptCanOpenWindowsAutomatically); settings.setJavaScriptCanOpenWindowsAutomatically(options.javaScriptCanOpenWindowsAutomatically);
settings.setBuiltInZoomControls(options.builtInZoomControls); settings.setBuiltInZoomControls(options.builtInZoomControls);
settings.setDisplayZoomControls(options.displayZoomControls); settings.setDisplayZoomControls(options.displayZoomControls);
@ -853,7 +850,7 @@ final public class InAppWebView extends InputAwareWebView {
} }
}; };
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && !options.useHybridComposition) {
checkContextMenuShouldBeClosedTask = new Runnable() { checkContextMenuShouldBeClosedTask = new Runnable() {
@Override @Override
public void run() { public void run() {
@ -1133,9 +1130,6 @@ final public class InAppWebView extends InputAwareWebView {
if (newOptionsMap.get("javaScriptEnabled") != null && options.javaScriptEnabled != newOptions.javaScriptEnabled) if (newOptionsMap.get("javaScriptEnabled") != null && options.javaScriptEnabled != newOptions.javaScriptEnabled)
settings.setJavaScriptEnabled(newOptions.javaScriptEnabled); settings.setJavaScriptEnabled(newOptions.javaScriptEnabled);
if (newOptionsMap.get("debuggingEnabled") != null && options.debuggingEnabled != newOptions.debuggingEnabled && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
setWebContentsDebuggingEnabled(newOptions.debuggingEnabled);
if (newOptionsMap.get("useShouldInterceptAjaxRequest") != null && options.useShouldInterceptAjaxRequest != newOptions.useShouldInterceptAjaxRequest) { if (newOptionsMap.get("useShouldInterceptAjaxRequest") != null && options.useShouldInterceptAjaxRequest != newOptions.useShouldInterceptAjaxRequest) {
String placeholderValue = newOptions.useShouldInterceptAjaxRequest ? "true" : "false"; String placeholderValue = newOptions.useShouldInterceptAjaxRequest ? "true" : "false";
String sourceJs = InAppWebView.enableVariableForShouldInterceptAjaxRequestJS.replace("$PLACEHOLDER_VALUE", placeholderValue); String sourceJs = InAppWebView.enableVariableForShouldInterceptAjaxRequestJS.replace("$PLACEHOLDER_VALUE", placeholderValue);
@ -1505,14 +1499,11 @@ final public class InAppWebView extends InputAwareWebView {
} }
@Override @Override
protected void onScrollChanged (int l, protected void onScrollChanged (int x,
int t, int y,
int oldl, int oldX,
int oldt) { int oldY) {
super.onScrollChanged(l, t, oldl, oldt); super.onScrollChanged(x, y, oldX, oldY);
int x = (int) (l/scale);
int y = (int) (t/scale);
if (floatingContextMenu != null) { if (floatingContextMenu != null) {
floatingContextMenu.setAlpha(0f); floatingContextMenu.setAlpha(0f);
@ -1662,12 +1653,18 @@ final public class InAppWebView extends InputAwareWebView {
@Override @Override
public ActionMode startActionMode(ActionMode.Callback callback) { public ActionMode startActionMode(ActionMode.Callback callback) {
if (options.useHybridComposition && !options.disableContextMenu && (contextMenu == null || contextMenu.keySet().size() == 0)) {
return super.startActionMode(callback);
}
return rebuildActionMode(super.startActionMode(callback), callback); return rebuildActionMode(super.startActionMode(callback), callback);
} }
@RequiresApi(api = Build.VERSION_CODES.M) @RequiresApi(api = Build.VERSION_CODES.M)
@Override @Override
public ActionMode startActionMode(ActionMode.Callback callback, int type) { public ActionMode startActionMode(ActionMode.Callback callback, int type) {
if (options.useHybridComposition && !options.disableContextMenu && (contextMenu == null || contextMenu.keySet().size() == 0)) {
return super.startActionMode(callback, type);
}
return rebuildActionMode(super.startActionMode(callback, type), callback); return rebuildActionMode(super.startActionMode(callback, type), callback);
} }
@ -1711,6 +1708,7 @@ final public class InAppWebView extends InputAwareWebView {
final MenuItem menuItem = actionMenu.getItem(i); final MenuItem menuItem = actionMenu.getItem(i);
final int itemId = menuItem.getItemId(); final int itemId = menuItem.getItemId();
final String itemTitle = menuItem.getTitle().toString(); final String itemTitle = menuItem.getTitle().toString();
TextView text = (TextView) LayoutInflater.from(this.getContext()) TextView text = (TextView) LayoutInflater.from(this.getContext())
.inflate(R.layout.floating_action_mode_item, this, false); .inflate(R.layout.floating_action_mode_item, this, false);
text.setText(itemTitle); text.setText(itemTitle);
@ -1855,7 +1853,7 @@ final public class InAppWebView extends InputAwareWebView {
" var clientRect = range.getClientRects();" + " var clientRect = range.getClientRects();" +
" if (clientRect.length > 0) {" + " if (clientRect.length > 0) {" +
" rangeY = clientRect[0].y;" + " rangeY = clientRect[0].y;" +
" } else if (document.activeElement) {" + " } else if (document.activeElement != null && document.activeElement.tagName.toLowerCase() !== 'iframe') {" +
" var boundingClientRect = document.activeElement.getBoundingClientRect();" + " var boundingClientRect = document.activeElement.getBoundingClientRect();" +
" rangeY = boundingClientRect.y;" + " rangeY = boundingClientRect.y;" +
" }" + " }" +
@ -1865,7 +1863,7 @@ final public class InAppWebView extends InputAwareWebView {
@Override @Override
public void onReceiveValue(String value) { public void onReceiveValue(String value) {
if (floatingContextMenu != null) { if (floatingContextMenu != null) {
if (value != null) { if (value != null && !value.equals("null")) {
int x = contextMenuPoint.x; int x = contextMenuPoint.x;
int y = (int) ((Float.parseFloat(value) * scale) + (floatingContextMenu.getHeight() / 3.5)); int y = (int) ((Float.parseFloat(value) * scale) + (floatingContextMenu.getHeight() / 3.5));
contextMenuPoint.y = y; contextMenuPoint.y = y;
@ -1873,6 +1871,7 @@ final public class InAppWebView extends InputAwareWebView {
} else { } else {
floatingContextMenu.setVisibility(View.VISIBLE); floatingContextMenu.setVisibility(View.VISIBLE);
floatingContextMenu.animate().alpha(1f).setDuration(100).setListener(null); floatingContextMenu.animate().alpha(1f).setDuration(100).setListener(null);
onFloatingActionGlobalLayout(contextMenuPoint.x, contextMenuPoint.y);
} }
} }
} }

View File

@ -3,9 +3,11 @@ package com.pichillilorenzo.flutter_inappwebview.InAppWebView;
import android.Manifest; import android.Manifest;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.app.Activity; import android.app.Activity;
import android.content.ContentResolver;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.res.AssetFileDescriptor;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.Color; import android.graphics.Color;
@ -46,6 +48,7 @@ import com.pichillilorenzo.flutter_inappwebview.Shared;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -72,7 +75,8 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
private static final int PICKER = 1; private static final int PICKER = 1;
private static final int PICKER_LEGACY = 3; private static final int PICKER_LEGACY = 3;
final String DEFAULT_MIME_TYPES = "*/*"; final String DEFAULT_MIME_TYPES = "*/*";
private static Uri outputFileUri; private static Uri videoOutputFileUri;
private static Uri imageOutputFileUri;
protected static final FrameLayout.LayoutParams FULLSCREEN_LAYOUT_PARAMS = new FrameLayout.LayoutParams( protected static final FrameLayout.LayoutParams FULLSCREEN_LAYOUT_PARAMS = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER); ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER);
@ -810,39 +814,37 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
// this filename instead // this filename instead
switch (requestCode) { switch (requestCode) {
case PICKER: case PICKER:
if (resultCode != RESULT_OK) { Uri[] results = null;
if (InAppWebViewFlutterPlugin.filePathCallback != null) { if (resultCode == RESULT_OK) {
InAppWebViewFlutterPlugin.filePathCallback.onReceiveValue(null); results = getSelectedFiles(data, resultCode);
} }
} else {
Uri result[] = this.getSelectedFiles(data, resultCode); if (InAppWebViewFlutterPlugin.filePathCallback != null) {
if (result != null) { InAppWebViewFlutterPlugin.filePathCallback.onReceiveValue(results);
InAppWebViewFlutterPlugin.filePathCallback.onReceiveValue(result);
} else {
InAppWebViewFlutterPlugin.filePathCallback.onReceiveValue(new Uri[]{outputFileUri});
}
} }
break; break;
case PICKER_LEGACY: case PICKER_LEGACY:
Uri result = resultCode != Activity.RESULT_OK ? null : data == null ? outputFileUri : data.getData(); Uri result = null;
if (resultCode == RESULT_OK) {
result = data != null ? data.getData() : getCapturedMediaFile();
}
InAppWebViewFlutterPlugin.filePathCallbackLegacy.onReceiveValue(result); InAppWebViewFlutterPlugin.filePathCallbackLegacy.onReceiveValue(result);
break; break;
} }
InAppWebViewFlutterPlugin.filePathCallback = null; InAppWebViewFlutterPlugin.filePathCallback = null;
InAppWebViewFlutterPlugin.filePathCallbackLegacy = null; InAppWebViewFlutterPlugin.filePathCallbackLegacy = null;
outputFileUri = null; imageOutputFileUri = null;
videoOutputFileUri = null;
return true; return true;
} }
private Uri[] getSelectedFiles(Intent data, int resultCode) { private Uri[] getSelectedFiles(Intent data, int resultCode) {
if (data == null) {
return null;
}
// we have one file selected // we have one file selected
if (data.getData() != null) { if (data != null && data.getData() != null) {
if (resultCode == RESULT_OK && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (resultCode == RESULT_OK && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return WebChromeClient.FileChooserParams.parseResult(resultCode, data); return WebChromeClient.FileChooserParams.parseResult(resultCode, data);
} else { } else {
@ -851,7 +853,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
} }
// we have multiple files selected // we have multiple files selected
if (data.getClipData() != null) { if (data != null && data.getClipData() != null) {
final int numSelectedFiles = data.getClipData().getItemCount(); final int numSelectedFiles = data.getClipData().getItemCount();
Uri[] result = new Uri[numSelectedFiles]; Uri[] result = new Uri[numSelectedFiles];
for (int i = 0; i < numSelectedFiles; i++) { for (int i = 0; i < numSelectedFiles; i++) {
@ -859,6 +861,40 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
} }
return result; return result;
} }
// we have a captured image or video file
Uri mediaUri = getCapturedMediaFile();
if (mediaUri != null) {
return new Uri[]{mediaUri};
}
return null;
}
private boolean isFileNotEmpty(Uri uri) {
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
long length;
try {
AssetFileDescriptor descriptor = activity.getContentResolver().openAssetFileDescriptor(uri, "r");
length = descriptor.getLength();
descriptor.close();
} catch (IOException e) {
return false;
}
return length > 0;
}
private Uri getCapturedMediaFile() {
if (imageOutputFileUri != null && isFileNotEmpty(imageOutputFileUri)) {
return imageOutputFileUri;
}
if (videoOutputFileUri != null && isFileNotEmpty(videoOutputFileUri)) {
return videoOutputFileUri;
}
return null; return null;
} }
@ -935,15 +971,15 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
private Intent getPhotoIntent() { private Intent getPhotoIntent() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
outputFileUri = getOutputUri(MediaStore.ACTION_IMAGE_CAPTURE); imageOutputFileUri = getOutputUri(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri); intent.putExtra(MediaStore.EXTRA_OUTPUT, imageOutputFileUri);
return intent; return intent;
} }
private Intent getVideoIntent() { private Intent getVideoIntent() {
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE); Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
outputFileUri = getOutputUri(MediaStore.ACTION_VIDEO_CAPTURE); videoOutputFileUri = getOutputUri(MediaStore.ACTION_VIDEO_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri); intent.putExtra(MediaStore.EXTRA_OUTPUT, videoOutputFileUri);
return intent; return intent;
} }
@ -971,6 +1007,20 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
return intent; return intent;
} }
private Boolean acceptsAny(String[] types) {
if (isArrayEmpty(types)) {
return true;
}
for (String type : types) {
if (type.equals("*/*")) {
return true;
}
}
return false;
}
private Boolean acceptsImages(String types) { private Boolean acceptsImages(String types) {
String mimeType = types; String mimeType = types;
if (types.matches("\\.\\w+")) { if (types.matches("\\.\\w+")) {
@ -981,7 +1031,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
private Boolean acceptsImages(String[] types) { private Boolean acceptsImages(String[] types) {
String[] mimeTypes = getAcceptedMimeType(types); String[] mimeTypes = getAcceptedMimeType(types);
return isArrayEmpty(mimeTypes) || arrayContainsString(mimeTypes, "image"); return acceptsAny(types) || arrayContainsString(mimeTypes, "image");
} }
private Boolean acceptsVideo(String types) { private Boolean acceptsVideo(String types) {
@ -994,7 +1044,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
private Boolean acceptsVideo(String[] types) { private Boolean acceptsVideo(String[] types) {
String[] mimeTypes = getAcceptedMimeType(types); String[] mimeTypes = getAcceptedMimeType(types);
return isArrayEmpty(mimeTypes) || arrayContainsString(mimeTypes, "video"); return acceptsAny(types) || arrayContainsString(mimeTypes, "video");
} }
private Boolean arrayContainsString(String[] array, String pattern) { private Boolean arrayContainsString(String[] array, String pattern) {
@ -1056,31 +1106,29 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
String prefix = ""; String prefix = "";
String suffix = ""; String suffix = "";
String dir = ""; String dir = "";
String filename = "";
if (intentType.equals(MediaStore.ACTION_IMAGE_CAPTURE)) { if (intentType.equals(MediaStore.ACTION_IMAGE_CAPTURE)) {
prefix = "image-"; prefix = "image";
suffix = ".jpg"; suffix = ".jpg";
dir = Environment.DIRECTORY_PICTURES; dir = Environment.DIRECTORY_PICTURES;
} else if (intentType.equals(MediaStore.ACTION_VIDEO_CAPTURE)) { } else if (intentType.equals(MediaStore.ACTION_VIDEO_CAPTURE)) {
prefix = "video-"; prefix = "video";
suffix = ".mp4"; suffix = ".mp4";
dir = Environment.DIRECTORY_MOVIES; dir = Environment.DIRECTORY_MOVIES;
} }
filename = prefix + String.valueOf(System.currentTimeMillis()) + suffix;
// for versions below 6.0 (23) we use the old File creation & permissions model // for versions below 6.0 (23) we use the old File creation & permissions model
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
// only this Directory works on all tested Android versions // only this Directory works on all tested Android versions
// ctx.getExternalFilesDir(dir) was failing on Android 5.0 (sdk 21) // ctx.getExternalFilesDir(dir) was failing on Android 5.0 (sdk 21)
File storageDir = Environment.getExternalStoragePublicDirectory(dir); File storageDir = Environment.getExternalStoragePublicDirectory(dir);
String filename = String.format("%s-%d%s", prefix, System.currentTimeMillis(), suffix);
return new File(storageDir, filename); return new File(storageDir, filename);
} }
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity; Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
File storageDir = activity.getApplicationContext().getExternalFilesDir(null); File storageDir = activity.getApplicationContext().getExternalFilesDir(null);
return File.createTempFile(filename, suffix, storageDir); return File.createTempFile(prefix, suffix, storageDir);
} }
private Boolean isArrayEmpty(String[] arr) { private Boolean isArrayEmpty(String[] arr) {

View File

@ -176,7 +176,9 @@ public class InAppWebViewClient extends WebViewClient {
if (webView.options.useOnLoadResource) { if (webView.options.useOnLoadResource) {
js += InAppWebView.resourceObserverJS.replaceAll("[\r\n]+", ""); js += InAppWebView.resourceObserverJS.replaceAll("[\r\n]+", "");
} }
js += InAppWebView.checkGlobalKeyDownEventToHideContextMenuJS.replaceAll("[\r\n]+", ""); if (!webView.options.useHybridComposition) {
js += InAppWebView.checkGlobalKeyDownEventToHideContextMenuJS.replaceAll("[\r\n]+", "");
}
js += InAppWebView.onWindowFocusEventJS.replaceAll("[\r\n]+", ""); js += InAppWebView.onWindowFocusEventJS.replaceAll("[\r\n]+", "");
js += InAppWebView.onWindowBlurEventJS.replaceAll("[\r\n]+", ""); js += InAppWebView.onWindowBlurEventJS.replaceAll("[\r\n]+", "");
js += InAppWebView.printJS.replaceAll("[\r\n]+", ""); js += InAppWebView.printJS.replaceAll("[\r\n]+", "");

View File

@ -26,7 +26,6 @@ public class InAppWebViewOptions implements Options<InAppWebView> {
public String userAgent = ""; public String userAgent = "";
public String applicationNameForUserAgent = ""; public String applicationNameForUserAgent = "";
public Boolean javaScriptEnabled = true; public Boolean javaScriptEnabled = true;
public Boolean debuggingEnabled = false;
public Boolean javaScriptCanOpenWindowsAutomatically = false; public Boolean javaScriptCanOpenWindowsAutomatically = false;
public Boolean mediaPlaybackRequiresUserGesture = true; public Boolean mediaPlaybackRequiresUserGesture = true;
public Integer minimumFontSize = 8; public Integer minimumFontSize = 8;
@ -44,6 +43,8 @@ public class InAppWebViewOptions implements Options<InAppWebView> {
public Boolean disableHorizontalScroll = false; public Boolean disableHorizontalScroll = false;
public Boolean disableContextMenu = false; public Boolean disableContextMenu = false;
public Boolean supportZoom = true; public Boolean supportZoom = true;
public Boolean allowFileAccessFromFileURLs = false;
public Boolean allowUniversalAccessFromFileURLs = false;
public Integer textZoom = 100; public Integer textZoom = 100;
public Boolean clearSessionCache = false; public Boolean clearSessionCache = false;
@ -56,8 +57,6 @@ public class InAppWebViewOptions implements Options<InAppWebView> {
public Integer mixedContentMode; public Integer mixedContentMode;
public Boolean allowContentAccess = true; public Boolean allowContentAccess = true;
public Boolean allowFileAccess = true; public Boolean allowFileAccess = true;
public Boolean allowFileAccessFromFileURLs = true;
public Boolean allowUniversalAccessFromFileURLs = true;
public String appCachePath; public String appCachePath;
public Boolean blockNetworkImage = false; public Boolean blockNetworkImage = false;
public Boolean blockNetworkLoads = false; public Boolean blockNetworkLoads = false;
@ -97,6 +96,7 @@ public class InAppWebViewOptions implements Options<InAppWebView> {
public Boolean useShouldInterceptRequest = false; public Boolean useShouldInterceptRequest = false;
public Boolean useOnRenderProcessGone = false; public Boolean useOnRenderProcessGone = false;
public Boolean disableDefaultErrorPage = false; public Boolean disableDefaultErrorPage = false;
public Boolean useHybridComposition = false;
@Override @Override
public InAppWebViewOptions parse(Map<String, Object> options) { public InAppWebViewOptions parse(Map<String, Object> options) {
@ -129,9 +129,6 @@ public class InAppWebViewOptions implements Options<InAppWebView> {
case "javaScriptEnabled": case "javaScriptEnabled":
javaScriptEnabled = (Boolean) value; javaScriptEnabled = (Boolean) value;
break; break;
case "debuggingEnabled":
debuggingEnabled = (Boolean) value;
break;
case "javaScriptCanOpenWindowsAutomatically": case "javaScriptCanOpenWindowsAutomatically":
javaScriptCanOpenWindowsAutomatically = (Boolean) value; javaScriptCanOpenWindowsAutomatically = (Boolean) value;
break; break;
@ -339,6 +336,9 @@ public class InAppWebViewOptions implements Options<InAppWebView> {
case "disableDefaultErrorPage": case "disableDefaultErrorPage":
disableDefaultErrorPage = (Boolean) value; disableDefaultErrorPage = (Boolean) value;
break; break;
case "useHybridComposition":
useHybridComposition = (Boolean) value;
break;
} }
} }
@ -355,7 +355,6 @@ public class InAppWebViewOptions implements Options<InAppWebView> {
options.put("userAgent", userAgent); options.put("userAgent", userAgent);
options.put("applicationNameForUserAgent", applicationNameForUserAgent); options.put("applicationNameForUserAgent", applicationNameForUserAgent);
options.put("javaScriptEnabled", javaScriptEnabled); options.put("javaScriptEnabled", javaScriptEnabled);
options.put("debuggingEnabled", debuggingEnabled);
options.put("javaScriptCanOpenWindowsAutomatically", javaScriptCanOpenWindowsAutomatically); options.put("javaScriptCanOpenWindowsAutomatically", javaScriptCanOpenWindowsAutomatically);
options.put("mediaPlaybackRequiresUserGesture", mediaPlaybackRequiresUserGesture); options.put("mediaPlaybackRequiresUserGesture", mediaPlaybackRequiresUserGesture);
options.put("minimumFontSize", minimumFontSize); options.put("minimumFontSize", minimumFontSize);
@ -425,6 +424,7 @@ public class InAppWebViewOptions implements Options<InAppWebView> {
options.put("useShouldInterceptRequest", useShouldInterceptRequest); options.put("useShouldInterceptRequest", useShouldInterceptRequest);
options.put("useOnRenderProcessGone", useOnRenderProcessGone); options.put("useOnRenderProcessGone", useOnRenderProcessGone);
options.put("disableDefaultErrorPage", disableDefaultErrorPage); options.put("disableDefaultErrorPage", disableDefaultErrorPage);
options.put("useHybridComposition", useHybridComposition);
return options; return options;
} }

View File

@ -16,6 +16,8 @@ import android.widget.ListPopupWindow;
* A WebView subclass that mirrors the same implementation hacks that the system WebView does in * A WebView subclass that mirrors the same implementation hacks that the system WebView does in
* order to correctly create an InputConnection. * order to correctly create an InputConnection.
* *
* These hacks are only needed in Android versions below N and exist to create an InputConnection
* on the WebView's dedicated input, or IME, thread. The majority of this proxying logic is in
* https://github.com/flutter/plugins/blob/master/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/InputAwareWebView.java * https://github.com/flutter/plugins/blob/master/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/InputAwareWebView.java
*/ */
public class InputAwareWebView extends WebView { public class InputAwareWebView extends WebView {
@ -234,9 +236,9 @@ public class InputAwareWebView extends WebView {
private boolean isCalledFromListPopupWindowShow() { private boolean isCalledFromListPopupWindowShow() {
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
for (int i = 0; i < stackTraceElements.length; i++) { for (StackTraceElement stackTraceElement : stackTraceElements) {
if (stackTraceElements[i].getClassName().equals(ListPopupWindow.class.getCanonicalName()) if (stackTraceElement.getClassName().equals(ListPopupWindow.class.getCanonicalName())
&& stackTraceElements[i].getMethodName().equals("show")) { && stackTraceElement.getMethodName().equals("show")) {
return true; return true;
} }
} }

View File

@ -72,6 +72,13 @@ public class InAppWebViewStatic implements MethodChannel.MethodCallHandler {
result.success(null); result.success(null);
} }
break; break;
case "setWebContentsDebuggingEnabled":
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
boolean debuggingEnabled = (boolean) call.argument("debuggingEnabled");
WebView.setWebContentsDebuggingEnabled(debuggingEnabled);
}
result.success(true);
break;
default: default:
result.notImplemented(); result.notImplemented();
} }

2
example/.flutter-plugins-dependencies Executable file → Normal file
View File

@ -1 +1 @@
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.4.4/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.10/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.1/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-5.4.11/","dependencies":[]}],"android":[{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.4.4/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.10/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.1/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-5.4.11/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.4+3/","dependencies":[]},{"name":"url_launcher_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-0.0.1+7/","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-0.0.1+1/","dependencies":[]}],"windows":[],"web":[{"name":"url_launcher_web","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_web-0.1.2/","dependencies":[]}]},"dependencyGraph":[{"name":"flutter_inappwebview","dependencies":[]},{"name":"e2e","dependencies":[]},{"name":"flutter_downloader","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos","path_provider_linux"]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"permission_handler","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_web","url_launcher_macos"]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_web","dependencies":[]}],"date_created":"2020-09-07 18:06:16.830498","version":"1.20.3"} {"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/.pub-cache/git/plugins-16f3281b04b0db12e609352b1c9544901392e428/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.27/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.1+1/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.4/","dependencies":[]}],"android":[{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/.pub-cache/git/plugins-16f3281b04b0db12e609352b1c9544901392e428/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.27/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.1+1/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.4/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.4+8/","dependencies":[]},{"name":"url_launcher_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-0.1.0-nullsafety.2/","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-0.0.1+2/","dependencies":[]},{"name":"url_launcher_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_linux-0.1.0-nullsafety.3/","dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-0.0.4+3/","dependencies":[]},{"name":"url_launcher_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-0.1.0-nullsafety.2/","dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":[]},{"name":"integration_test","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos","path_provider_linux","path_provider_windows"]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"permission_handler","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_linux","url_launcher_macos","url_launcher_windows"]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2021-01-29 00:53:56.206541","version":"1.26.0-13.0.pre.194"}

View File

@ -1,15 +1,16 @@
package com.pichillilorenzo.flutterwebviewexample; package com.pichillilorenzo.flutterwebviewexample;
import android.os.Bundle; import android.os.Bundle;
import dev.flutter.plugins.e2e.E2EPlugin; import dev.flutter.plugins.integration_test.IntegrationTestPlugin;
import io.flutter.app.FlutterActivity;
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin; import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
public class EmbedderV1Activity extends FlutterActivity { @SuppressWarnings("deprecation")
public class EmbedderV1Activity extends io.flutter.app.FlutterActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
E2EPlugin.registerWith(registrarFor("dev.flutter.plugins.e2e.E2EPlugin")); IntegrationTestPlugin.registerWith(
registrarFor("dev.flutter.plugins.integration_test.IntegrationTestPlugin"));
InAppWebViewFlutterPlugin.registerWith( InAppWebViewFlutterPlugin.registerWith(
registrarFor("com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin")); registrarFor("com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin"));
} }

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@
final environment = {"NODE_SERVER_IP":"192.168.1.129"};

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,4 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig" #include "Generated.xcconfig"
FLUTTER_BUILD_MODE=debug FLUTTER_BUILD_MODE=debug

View File

@ -1,18 +0,0 @@
#
# NOTE: This podspec is NOT to be published. It is only used as a local source!
#
Pod::Spec.new do |s|
s.name = 'Flutter'
s.version = '1.0.0'
s.summary = 'High-performance, high-fidelity mobile apps.'
s.description = <<-DESC
Flutter provides an easy and productive way to build and deploy high-performance mobile apps for Android and iOS.
DESC
s.homepage = 'https://flutter.io'
s.license = { :type => 'MIT' }
s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' }
s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s }
s.ios.deployment_target = '8.0'
s.vendored_frameworks = 'Flutter.framework'
end

View File

@ -1,2 +1,3 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig" #include "Generated.xcconfig"

View File

@ -2,14 +2,12 @@
# This is a generated file; do not edit or check into version control. # This is a generated file; do not edit or check into version control.
export "FLUTTER_ROOT=/Users/lorenzopichilli/flutter" export "FLUTTER_ROOT=/Users/lorenzopichilli/flutter"
export "FLUTTER_APPLICATION_PATH=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example" export "FLUTTER_APPLICATION_PATH=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example"
export "FLUTTER_TARGET=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example/lib/main.dart" export "FLUTTER_TARGET=lib/main.dart"
export "FLUTTER_BUILD_DIR=build" export "FLUTTER_BUILD_DIR=build"
export "SYMROOT=${SOURCE_ROOT}/../build/ios" export "SYMROOT=${SOURCE_ROOT}/../build/ios"
export "OTHER_LDFLAGS=$(inherited) -framework Flutter"
export "FLUTTER_FRAMEWORK_DIR=/Users/lorenzopichilli/flutter/bin/cache/artifacts/engine/ios"
export "FLUTTER_BUILD_NAME=1.0.0" export "FLUTTER_BUILD_NAME=1.0.0"
export "FLUTTER_BUILD_NUMBER=1" export "FLUTTER_BUILD_NUMBER=1"
export "DART_OBFUSCATION=false" export "DART_OBFUSCATION=false"
export "TRACK_WIDGET_CREATION=true" export "TRACK_WIDGET_CREATION=false"
export "TREE_SHAKE_ICONS=false" export "TREE_SHAKE_ICONS=false"
export "PACKAGE_CONFIG=.packages" export "PACKAGE_CONFIG=.packages"

View File

@ -254,19 +254,17 @@
); );
inputPaths = ( inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
"${PODS_ROOT}/../Flutter/Flutter.framework",
"${BUILT_PRODUCTS_DIR}/e2e/e2e.framework",
"${BUILT_PRODUCTS_DIR}/flutter_downloader/flutter_downloader.framework", "${BUILT_PRODUCTS_DIR}/flutter_downloader/flutter_downloader.framework",
"${BUILT_PRODUCTS_DIR}/flutter_inappwebview/flutter_inappwebview.framework", "${BUILT_PRODUCTS_DIR}/flutter_inappwebview/flutter_inappwebview.framework",
"${BUILT_PRODUCTS_DIR}/integration_test/integration_test.framework",
"${BUILT_PRODUCTS_DIR}/path_provider/path_provider.framework", "${BUILT_PRODUCTS_DIR}/path_provider/path_provider.framework",
"${BUILT_PRODUCTS_DIR}/url_launcher/url_launcher.framework", "${BUILT_PRODUCTS_DIR}/url_launcher/url_launcher.framework",
); );
name = "[CP] Embed Pods Frameworks"; name = "[CP] Embed Pods Frameworks";
outputPaths = ( outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/e2e.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_downloader.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_downloader.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_inappwebview.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_inappwebview.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/integration_test.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher.framework",
); );
@ -451,7 +449,7 @@
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Flutter", "$(PROJECT_DIR)/Flutter",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.pichillilorenzo.flutter_inappwebviewExample; PRODUCT_BUNDLE_IDENTIFIER = "flutter-inappwebviewExample";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";

View File

@ -2,6 +2,6 @@
<Workspace <Workspace
version = "1.0"> version = "1.0">
<FileRef <FileRef
location = "group:Runner.xcodeproj"> location = "self:">
</FileRef> </FileRef>
</Workspace> </Workspace>

View File

@ -2,54 +2,52 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>NSLocationAlwaysUsageDescription</key> <key>CFBundleDevelopmentRegion</key>
<string>Need location</string> <string>en</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Need location</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Need location</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleName</key>
<string>flutter_inappwebview_example</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>remote-notification</string>
</array>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string> <string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>flutter_inappwebview_example</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>NSAppTransportSecurity</key> <key>NSAppTransportSecurity</key>
<dict> <dict>
<key>NSAllowsLocalNetworking</key> <key>NSAllowsLocalNetworking</key>
<true/> <true/>
</dict> </dict>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Need location</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Need location</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Need location</string>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>remote-notification</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key> <key>UISupportedInterfaceOrientations</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>io.flutter.embedded_views_preview</key>
<true/>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>UISupportedInterfaceOrientations~ipad</key> <key>UISupportedInterfaceOrientations~ipad</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationPortrait</string>
@ -57,7 +55,13 @@
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>CFBundleShortVersionString</key> <key>NSBonjourServices</key>
<string>$(FLUTTER_BUILD_NAME)</string> <array>
<string></string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>NSLocalNetworkUsageDescription</key>
<string>Allow Flutter tools on your computer to connect and debug your application.</string>
</dict> </dict>
</plist> </plist>

View File

@ -11,7 +11,7 @@ class HeadlessInAppWebViewExampleScreen extends StatefulWidget {
} }
class _HeadlessInAppWebViewExampleScreenState extends State<HeadlessInAppWebViewExampleScreen> { class _HeadlessInAppWebViewExampleScreenState extends State<HeadlessInAppWebViewExampleScreen> {
HeadlessInAppWebView headlessWebView; HeadlessInAppWebView? headlessWebView;
String url = ""; String url = "";
@override @override
@ -22,7 +22,7 @@ class _HeadlessInAppWebViewExampleScreenState extends State<HeadlessInAppWebView
initialUrl: "https://flutter.dev/", initialUrl: "https://flutter.dev/",
initialOptions: InAppWebViewGroupOptions( initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions( crossPlatform: InAppWebViewOptions(
debuggingEnabled: true,
), ),
), ),
onWebViewCreated: (controller) { onWebViewCreated: (controller) {
@ -34,19 +34,19 @@ class _HeadlessInAppWebViewExampleScreenState extends State<HeadlessInAppWebView
onLoadStart: (controller, url) async { onLoadStart: (controller, url) async {
print("onLoadStart $url"); print("onLoadStart $url");
setState(() { setState(() {
this.url = url; this.url = url ?? '';
}); });
}, },
onLoadStop: (controller, url) async { onLoadStop: (controller, url) async {
print("onLoadStop $url"); print("onLoadStop $url");
setState(() { setState(() {
this.url = url; this.url = url ?? '';
}); });
}, },
onUpdateVisitedHistory: (InAppWebViewController controller, String url, bool androidIsReload) { onUpdateVisitedHistory: (controller, url, androidIsReload) {
print("onUpdateVisitedHistory $url"); print("onUpdateVisitedHistory $url");
setState(() { setState(() {
this.url = url; this.url = url ?? '';
}); });
}, },
); );
@ -55,7 +55,7 @@ class _HeadlessInAppWebViewExampleScreenState extends State<HeadlessInAppWebView
@override @override
void dispose() { void dispose() {
super.dispose(); super.dispose();
headlessWebView.dispose(); headlessWebView?.dispose();
} }
@override @override
@ -76,8 +76,8 @@ class _HeadlessInAppWebViewExampleScreenState extends State<HeadlessInAppWebView
Center( Center(
child: RaisedButton( child: RaisedButton(
onPressed: () async { onPressed: () async {
await headlessWebView.dispose(); await headlessWebView?.dispose();
await headlessWebView.run(); await headlessWebView?.run();
}, },
child: Text("Run HeadlessInAppWebView")), child: Text("Run HeadlessInAppWebView")),
), ),
@ -85,7 +85,7 @@ class _HeadlessInAppWebViewExampleScreenState extends State<HeadlessInAppWebView
child: RaisedButton( child: RaisedButton(
onPressed: () async { onPressed: () async {
try { try {
await headlessWebView.webViewController.evaluateJavascript(source: """console.log('Here is the message!');"""); await headlessWebView?.webViewController.evaluateJavascript(source: """console.log('Here is the message!');""");
} on MissingPluginException catch(e) { } on MissingPluginException catch(e) {
print("HeadlessInAppWebView is not running. Click on \"Run HeadlessInAppWebView\"!"); print("HeadlessInAppWebView is not running. Click on \"Run HeadlessInAppWebView\"!");
} }
@ -95,7 +95,7 @@ class _HeadlessInAppWebViewExampleScreenState extends State<HeadlessInAppWebView
Center( Center(
child: RaisedButton( child: RaisedButton(
onPressed: () { onPressed: () {
headlessWebView.dispose(); headlessWebView?.dispose();
}, },
child: Text("Dispose HeadlessInAppWebView")), child: Text("Dispose HeadlessInAppWebView")),
) )

View File

@ -12,22 +12,22 @@ class MyInAppBrowser extends InAppBrowser {
} }
@override @override
Future onLoadStart(String url) async { Future onLoadStart(url) async {
print("\n\nStarted $url\n\n"); print("\n\nStarted $url\n\n");
} }
@override @override
Future onLoadStop(String url) async { Future onLoadStop(url) async {
print("\n\nStopped $url\n\n"); print("\n\nStopped $url\n\n");
} }
@override @override
void onLoadError(String url, int code, String message) { void onLoadError(url, code, message) {
print("Can't load $url.. Error: $message"); print("Can't load $url.. Error: $message");
} }
@override @override
void onProgressChanged(int progress) { void onProgressChanged(progress) {
print("Progress: $progress"); print("Progress: $progress");
} }
@ -38,27 +38,27 @@ class MyInAppBrowser extends InAppBrowser {
@override @override
Future<ShouldOverrideUrlLoadingAction> shouldOverrideUrlLoading( Future<ShouldOverrideUrlLoadingAction> shouldOverrideUrlLoading(
ShouldOverrideUrlLoadingRequest shouldOverrideUrlLoadingRequest) async { shouldOverrideUrlLoadingRequest) async {
print("\n\nOverride ${shouldOverrideUrlLoadingRequest.url}\n\n"); print("\n\nOverride ${shouldOverrideUrlLoadingRequest.url}\n\n");
return ShouldOverrideUrlLoadingAction.ALLOW; return ShouldOverrideUrlLoadingAction.ALLOW;
} }
@override @override
void onLoadResource(LoadedResource response) { void onLoadResource(response) {
print("Started at: " + print("Started at: " +
response.startTime.toString() + response.startTime.toString() +
"ms ---> duration: " + "ms ---> duration: " +
response.duration.toString() + response.duration.toString() +
"ms " + "ms " +
response.url); response.url!);
} }
@override @override
void onConsoleMessage(ConsoleMessage consoleMessage) { void onConsoleMessage(consoleMessage) {
print(""" print("""
console output: console output:
message: ${consoleMessage.message} message: ${consoleMessage.message}
messageLevel: ${consoleMessage.messageLevel.toValue()} messageLevel: ${consoleMessage.messageLevel!.toValue()}
"""); """);
} }
} }
@ -96,7 +96,6 @@ class _InAppBrowserExampleScreenState extends State<InAppBrowserExampleScreen> {
options: InAppBrowserClassOptions( options: InAppBrowserClassOptions(
inAppWebViewGroupOptions: InAppWebViewGroupOptions( inAppWebViewGroupOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions( crossPlatform: InAppWebViewOptions(
debuggingEnabled: true,
useShouldOverrideUrlLoading: true, useShouldOverrideUrlLoading: true,
useOnLoadResource: true, useOnLoadResource: true,
)))); ))));

View File

@ -1,4 +1,3 @@
import 'dart:developer';
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -14,8 +13,8 @@ class InAppWebViewExampleScreen extends StatefulWidget {
} }
class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> { class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
InAppWebViewController webView; InAppWebViewController? webView;
ContextMenu contextMenu; late ContextMenu contextMenu;
String url = ""; String url = "";
double progress = 0; double progress = 0;
CookieManager _cookieManager = CookieManager.instance(); CookieManager _cookieManager = CookieManager.instance();
@ -28,8 +27,8 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
menuItems: [ menuItems: [
ContextMenuItem(androidId: 1, iosId: "1", title: "Special", action: () async { ContextMenuItem(androidId: 1, iosId: "1", title: "Special", action: () async {
print("Menu item Special clicked!"); print("Menu item Special clicked!");
print(await webView.getSelectedText()); print(await webView?.getSelectedText());
await webView.clearFocus(); await webView?.clearFocus();
}) })
], ],
options: ContextMenuOptions( options: ContextMenuOptions(
@ -38,7 +37,7 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
onCreateContextMenu: (hitTestResult) async { onCreateContextMenu: (hitTestResult) async {
print("onCreateContextMenu"); print("onCreateContextMenu");
print(hitTestResult.extra); print(hitTestResult.extra);
print(await webView.getSelectedText()); print(await webView?.getSelectedText());
}, },
onHideContextMenu: () { onHideContextMenu: () {
print("onHideContextMenu"); print("onHideContextMenu");
@ -81,26 +80,25 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
BoxDecoration(border: Border.all(color: Colors.blueAccent)), BoxDecoration(border: Border.all(color: Colors.blueAccent)),
child: InAppWebView( child: InAppWebView(
// contextMenu: contextMenu, // contextMenu: contextMenu,
initialUrl: "https://github.com/flutter", initialUrl: "https://flutter.dev/",
// initialFile: "assets/index.html", // initialFile: "assets/index.html",
initialHeaders: {}, initialHeaders: {},
initialOptions: InAppWebViewGroupOptions( initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions( crossPlatform: InAppWebViewOptions(
debuggingEnabled: true, useShouldOverrideUrlLoading: false,
useShouldOverrideUrlLoading: true,
), ),
android: AndroidInAppWebViewOptions( android: AndroidInAppWebViewOptions(
useHybridComposition: true useHybridComposition: true
) )
), ),
onWebViewCreated: (InAppWebViewController controller) { onWebViewCreated: (controller) {
webView = controller; webView = controller;
print("onWebViewCreated"); print("onWebViewCreated");
}, },
onLoadStart: (InAppWebViewController controller, String url) { onLoadStart: (controller, url) {
print("onLoadStart $url"); print("onLoadStart $url");
setState(() { setState(() {
this.url = url; this.url = url ?? '';
}); });
}, },
shouldOverrideUrlLoading: (controller, shouldOverrideUrlLoadingRequest) async { shouldOverrideUrlLoading: (controller, shouldOverrideUrlLoadingRequest) async {
@ -122,21 +120,21 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
return ShouldOverrideUrlLoadingAction.ALLOW; return ShouldOverrideUrlLoadingAction.ALLOW;
}, },
onLoadStop: (InAppWebViewController controller, String url) async { onLoadStop: (controller, url) async {
print("onLoadStop $url"); print("onLoadStop $url");
setState(() { setState(() {
this.url = url; this.url = url ?? '';
}); });
}, },
onProgressChanged: (InAppWebViewController controller, int progress) { onProgressChanged: (controller, progress) {
setState(() { setState(() {
this.progress = progress / 100; this.progress = progress / 100;
}); });
}, },
onUpdateVisitedHistory: (InAppWebViewController controller, String url, bool androidIsReload) { onUpdateVisitedHistory: (controller, url, androidIsReload) {
print("onUpdateVisitedHistory $url"); print("onUpdateVisitedHistory $url");
setState(() { setState(() {
this.url = url; this.url = url ?? '';
}); });
}, },
onConsoleMessage: (controller, consoleMessage) { onConsoleMessage: (controller, consoleMessage) {
@ -151,25 +149,19 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
RaisedButton( RaisedButton(
child: Icon(Icons.arrow_back), child: Icon(Icons.arrow_back),
onPressed: () { onPressed: () {
if (webView != null) { webView?.goBack();
webView.goBack();
}
}, },
), ),
RaisedButton( RaisedButton(
child: Icon(Icons.arrow_forward), child: Icon(Icons.arrow_forward),
onPressed: () { onPressed: () {
if (webView != null) { webView?.goForward();
webView.goForward();
}
}, },
), ),
RaisedButton( RaisedButton(
child: Icon(Icons.refresh), child: Icon(Icons.refresh),
onPressed: () { onPressed: () {
if (webView != null) { webView?.reload();
webView.reload();
}
}, },
), ),
], ],

View File

@ -1,6 +1,8 @@
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_inappwebview_example/chrome_safari_browser_example.screen.dart'; import 'package:flutter_inappwebview_example/chrome_safari_browser_example.screen.dart';
import 'package:flutter_inappwebview_example/headless_in_app_webview.screen.dart'; import 'package:flutter_inappwebview_example/headless_in_app_webview.screen.dart';
@ -15,10 +17,13 @@ Future main() async {
// await Permission.camera.request(); // await Permission.camera.request();
// await Permission.storage.request(); // await Permission.storage.request();
// await localhostServer.start(); // await localhostServer.start();
if (Platform.isAndroid) {
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
}
runApp(MyApp()); runApp(MyApp());
} }
Drawer myDrawer({@required BuildContext context}) { Drawer myDrawer({required BuildContext context}) {
return Drawer( return Drawer(
child: ListView( child: ListView(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,

View File

@ -10,8 +10,8 @@ description: Demonstrates how to use the flutter_inappwebview plugin.
version: 1.0.0+1 version: 1.0.0+1
environment: environment:
sdk: ">=2.0.0-dev.68.0 <3.0.0" sdk: ">=2.12.0-0 <3.0.0"
flutter: ">=1.10.0 <2.0.0" flutter: ">=1.22.0"
dependencies: dependencies:
flutter: flutter:
@ -19,20 +19,26 @@ dependencies:
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2 cupertino_icons: ^1.0.2
flutter_downloader: ^1.4.4 flutter_downloader: ^1.5.2
path_provider: ^1.6.9 path_provider: ^1.6.27
permission_handler: ^5.0.0+hotfix.6 permission_handler: ^5.0.1+1
url_launcher: ^5.4.11 url_launcher: ^6.0.0-nullsafety.4
# connectivity: ^0.4.5+6 # connectivity: ^0.4.5+6
flutter_inappwebview: flutter_inappwebview:
path: ../ path: ../
dev_dependencies: dev_dependencies:
e2e: "^0.2.0" flutter_test:
sdk: flutter
flutter_driver: flutter_driver:
sdk: flutter sdk: flutter
test: any # integration_test: ^1.0.2+1
integration_test:
git:
url: https://github.com/flutter/plugins.git
path: packages/integration_test
pedantic: ^1.8.0
# For information on the generic Dart part of this file, see the # For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec # following page: https://www.dartlang.org/tools/pub/pubspec
@ -53,6 +59,8 @@ flutter:
- assets/css/ - assets/css/
- assets/images/ - assets/images/
- assets/favicon.ico - assets/favicon.ico
- assets/sample_audio.ogg
- assets/sample_video.mp4
- test_assets/certificate.pfx - test_assets/certificate.pfx
- test_assets/in_app_webview_initial_file_test.html - test_assets/in_app_webview_initial_file_test.html
- test_assets/in_app_webview_on_load_resource_test.html - test_assets/in_app_webview_on_load_resource_test.html
@ -64,6 +72,8 @@ flutter:
- test_assets/css/ - test_assets/css/
- test_assets/images/ - test_assets/images/
- test_assets/favicon.ico - test_assets/favicon.ico
- test_assets/sample_audio.ogg
- test_assets/sample_video.mp4
# To add assets to your application, add an assets section, like this: # To add assets to your application, add an assets section, like this:
# assets: # assets:

Binary file not shown.

Binary file not shown.

View File

@ -1 +0,0 @@
final environment = {"NODE_SERVER_IP":"192.168.1.122"};

View File

@ -1,11 +0,0 @@
import 'package:flutter_driver/driver_extension.dart';
import 'main_test.dart' as app;
void main() {
// This line enables the extension.
enableFlutterDriverExtension();
// Call the `main()` function of the app, or call `runApp` with
// any widget you are interested in testing.
app.main();
}

View File

@ -1,532 +0,0 @@
// Imports the Flutter Driver API.
import 'dart:async';
import 'dart:io';
import 'package:flutter_driver/flutter_driver.dart';
import 'package:test/test.dart';
import '.env.dart';
void main() {
group('Flutter InAppWebView', () {
FlutterDriver driver;
// Connect to the Flutter driver before running any tests.
setUpAll(() async {
driver = await FlutterDriver.connect();
await driver.setTextEntryEmulation(enabled: true);
});
// Close the connection to the driver after the tests have completed.
tearDownAll(() async {
if (driver != null) {
driver.close();
}
});
test('InAppWebViewInitialUrlTest', () async {
final appBarTitle = find.byValueKey('AppBarTitle');
final sideMenuButton = find.byValueKey('SideMenu');
final listTiles = find.byValueKey('ListTiles');
final nextTest = find.byValueKey('InAppWebViewInitialFileTest');
while((await driver.getText(appBarTitle)) == "InAppWebViewInitialUrlTest") {
await Future.delayed(const Duration(milliseconds: 500));
}
String url = await driver.getText(appBarTitle);
expect(url, "https://flutter.dev/");
await driver.tap(sideMenuButton);
await driver.scrollUntilVisible(listTiles, nextTest, dyScroll: -300.0);
await driver.tap(nextTest);
}, timeout: new Timeout(new Duration(minutes: 5)));
test('InAppWebViewInitialFileTest', () async {
final appBarTitle = find.byValueKey('AppBarTitle');
final sideMenuButton = find.byValueKey('SideMenu');
final listTiles = find.byValueKey('ListTiles');
final nextTest = find.byValueKey('InAppWebViewInitialDataTest');
while((await driver.getText(appBarTitle)) == "InAppWebViewInitialFileTest") {
await Future.delayed(const Duration(milliseconds: 500));
}
String title = await driver.getText(appBarTitle);
expect(title, "true");
await driver.tap(sideMenuButton);
await driver.scrollUntilVisible(listTiles, nextTest, dyScroll: -300.0);
await driver.tap(nextTest);
}, timeout: new Timeout(new Duration(minutes: 5)));
test('InAppWebViewInitialDataTest', () async {
final appBarTitle = find.byValueKey('AppBarTitle');
final sideMenuButton = find.byValueKey('SideMenu');
final listTiles = find.byValueKey('ListTiles');
final nextTest = find.byValueKey('InAppWebViewOnProgressChangedTest');
while((await driver.getText(appBarTitle)) == "InAppWebViewInitialDataTest") {
await Future.delayed(const Duration(milliseconds: 500));
}
String title = await driver.getText(appBarTitle);
expect(title, "true");
await driver.tap(sideMenuButton);
await driver.scrollUntilVisible(listTiles, nextTest, dyScroll: -300.0);
await driver.tap(nextTest);
}, timeout: new Timeout(new Duration(minutes: 5)));
test('InAppWebViewOnProgressChangedTest', () async {
final appBarTitle = find.byValueKey('AppBarTitle');
final sideMenuButton = find.byValueKey('SideMenu');
final listTiles = find.byValueKey('ListTiles');
final nextTest = find.byValueKey('InAppWebViewOnScrollChangedTest');
while((await driver.getText(appBarTitle)) == "InAppWebViewOnProgressChangedTest") {
await Future.delayed(const Duration(milliseconds: 500));
}
String title = await driver.getText(appBarTitle);
expect(title, "true");
await driver.tap(sideMenuButton);
await driver.scrollUntilVisible(listTiles, nextTest, dyScroll: -300.0);
await driver.tap(nextTest);
}, timeout: new Timeout(new Duration(minutes: 5)));
test('InAppWebViewOnScrollChangedTest', () async {
final appBarTitle = find.byValueKey('AppBarTitle');
final sideMenuButton = find.byValueKey('SideMenu');
final listTiles = find.byValueKey('ListTiles');
final nextTest = find.byValueKey('InAppWebViewOnLoadResourceTest');
while((await driver.getText(appBarTitle)) == "InAppWebViewOnScrollChangedTest") {
await Future.delayed(const Duration(milliseconds: 500));
}
String title = await driver.getText(appBarTitle);
expect(title, "true");
await driver.tap(sideMenuButton);
await driver.scrollUntilVisible(listTiles, nextTest, dyScroll: -300.0);
await driver.tap(nextTest);
}, timeout: new Timeout(new Duration(minutes: 5)));
test('InAppWebViewOnLoadResourceTest', () async {
List<String> resourceList = [
"https://getbootstrap.com/docs/4.3/dist/css/bootstrap.min.css",
"https://code.jquery.com/jquery-3.3.1.min.js",
"https://via.placeholder.com/100x50"
];
final appBarTitle = find.byValueKey('AppBarTitle');
final sideMenuButton = find.byValueKey('SideMenu');
final listTiles = find.byValueKey('ListTiles');
final nextTest = find.byValueKey('InAppWebViewJavaScriptHandlerTest');
while((await driver.getText(appBarTitle)) == "InAppWebViewOnLoadResourceTest") {
await Future.delayed(const Duration(milliseconds: 500));
}
String title = await driver.getText(appBarTitle);
for (String resource in resourceList) {
expect(true, title.contains(resource));
}
await driver.tap(sideMenuButton);
await driver.scrollUntilVisible(listTiles, nextTest, dyScroll: -300.0);
await driver.tap(nextTest);
}, timeout: new Timeout(new Duration(minutes: 5)));
test('InAppWebViewJavaScriptHandlerTest', () async {
final appBarTitle = find.byValueKey('AppBarTitle');
final sideMenuButton = find.byValueKey('SideMenu');
final listTiles = find.byValueKey('ListTiles');
final nextTest = find.byValueKey('InAppWebViewAjaxTest');
while((await driver.getText(appBarTitle)) == "InAppWebViewJavaScriptHandlerTest") {
await Future.delayed(const Duration(milliseconds: 500));
}
String title = await driver.getText(appBarTitle);
expect(true, !title.contains("false"));
await driver.tap(sideMenuButton);
await driver.scrollUntilVisible(listTiles, nextTest, dyScroll: -300.0);
await driver.tap(nextTest);
}, timeout: new Timeout(new Duration(minutes: 5)));
test('InAppWebViewAjaxTest', () async {
final appBarTitle = find.byValueKey('AppBarTitle');
final sideMenuButton = find.byValueKey('SideMenu');
final listTiles = find.byValueKey('ListTiles');
final nextTest = find.byValueKey('InAppWebViewFetchTest');
while((await driver.getText(appBarTitle)) == "InAppWebViewAjaxTest") {
await Future.delayed(const Duration(milliseconds: 500));
}
String title = await driver.getText(appBarTitle);
expect(title, "Lorenzo Pichilli Lorenzo Pichilli");
await driver.tap(sideMenuButton);
await driver.scrollUntilVisible(listTiles, nextTest, dyScroll: -300.0);
await driver.tap(nextTest);
}, timeout: new Timeout(new Duration(minutes: 5)));
test('InAppWebViewFetchTest', () async {
final appBarTitle = find.byValueKey('AppBarTitle');
final sideMenuButton = find.byValueKey('SideMenu');
final listTiles = find.byValueKey('ListTiles');
final nextTest = find.byValueKey('InAppWebViewOnLoadResourceCustomSchemeTest');
while((await driver.getText(appBarTitle)) == "InAppWebViewFetchTest") {
await Future.delayed(const Duration(milliseconds: 500));
}
String title = await driver.getText(appBarTitle);
expect(true, title.contains("Lorenzo Pichilli") && title.contains("200"));
await driver.tap(sideMenuButton);
await driver.scrollUntilVisible(listTiles, nextTest, dyScroll: -300.0);
await driver.tap(nextTest);
}, timeout: new Timeout(new Duration(minutes: 5)));
test('InAppWebViewOnLoadResourceCustomSchemeTest', () async {
final appBarTitle = find.byValueKey('AppBarTitle');
final sideMenuButton = find.byValueKey('SideMenu');
final listTiles = find.byValueKey('ListTiles');
final nextTest = find.byValueKey('InAppWebViewShouldOverrideUrlLoadingTest');
while((await driver.getText(appBarTitle)) == "InAppWebViewOnLoadResourceCustomSchemeTest") {
await Future.delayed(const Duration(milliseconds: 500));
}
String title = await driver.getText(appBarTitle);
expect(title, "true");
await driver.tap(sideMenuButton);
await driver.scrollUntilVisible(listTiles, nextTest, dyScroll: -300.0);
await driver.tap(nextTest);
}, timeout: new Timeout(new Duration(minutes: 5)));
test('InAppWebViewShouldOverrideUrlLoadingTest', () async {
final appBarTitle = find.byValueKey('AppBarTitle');
final sideMenuButton = find.byValueKey('SideMenu');
final listTiles = find.byValueKey('ListTiles');
final nextTest = find.byValueKey('InAppWebViewOnConsoleMessageTest');
while((await driver.getText(appBarTitle)) == "InAppWebViewShouldOverrideUrlLoadingTest") {
await Future.delayed(const Duration(milliseconds: 500));
}
String url = await driver.getText(appBarTitle);
expect(url, "https://flutter.dev/");
await driver.tap(sideMenuButton);
await driver.scrollUntilVisible(listTiles, nextTest, dyScroll: -300.0);
await driver.tap(nextTest);
}, timeout: new Timeout(new Duration(minutes: 5)));
test('InAppWebViewOnConsoleMessageTest', () async {
final appBarTitle = find.byValueKey('AppBarTitle');
final sideMenuButton = find.byValueKey('SideMenu');
final listTiles = find.byValueKey('ListTiles');
final nextTest = find.byValueKey('InAppWebViewOnDownloadStartTest');
while((await driver.getText(appBarTitle)) == "InAppWebViewOnConsoleMessageTest") {
await Future.delayed(const Duration(milliseconds: 500));
}
String title = await driver.getText(appBarTitle);
expect(title, "message LOG");
await driver.tap(sideMenuButton);
await driver.scrollUntilVisible(listTiles, nextTest, dyScroll: -300.0);
await driver.tap(nextTest);
}, timeout: new Timeout(new Duration(minutes: 5)));
test('InAppWebViewOnDownloadStartTest', () async {
final appBarTitle = find.byValueKey('AppBarTitle');
final sideMenuButton = find.byValueKey('SideMenu');
final listTiles = find.byValueKey('ListTiles');
final nextTest = find.byValueKey('InAppWebViewOnCreateWindowTest');
while((await driver.getText(appBarTitle)) == "InAppWebViewOnDownloadStartTest") {
await Future.delayed(const Duration(milliseconds: 500));
}
String url = await driver.getText(appBarTitle);
expect(url, "http://${environment["NODE_SERVER_IP"]}:8082/test-download-file");
await driver.tap(sideMenuButton);
await driver.scrollUntilVisible(listTiles, nextTest, dyScroll: -300.0);
await driver.tap(nextTest);
}, timeout: new Timeout(new Duration(minutes: 5)));
test('InAppWebViewOnCreateWindowTest', () async {
final appBarTitle = find.byValueKey('AppBarTitle');
final sideMenuButton = find.byValueKey('SideMenu');
final listTiles = find.byValueKey('ListTiles');
final nextTest = find.byValueKey('InAppWebViewOnJsDialogTest');
while((await driver.getText(appBarTitle)) == "InAppWebViewOnCreateWindowTest") {
await Future.delayed(const Duration(milliseconds: 500));
}
String url = await driver.getText(appBarTitle);
expect(url, "https://flutter.dev/");
await driver.tap(sideMenuButton);
await driver.scrollUntilVisible(listTiles, nextTest, dyScroll: -300.0);
await driver.tap(nextTest);
}, timeout: new Timeout(new Duration(minutes: 5)));
test('InAppWebViewOnJsDialogTest', () async {
final appBarTitle = find.byValueKey('AppBarTitle');
final alertButtonOk = find.byValueKey('AlertButtonOk');
final confirmButtonCancel = find.byValueKey('ConfirmButtonCancel');
final confirmButtonOk = find.byValueKey('ConfirmButtonOk');
final promptTextField = find.byValueKey('PromptTextField');
final promptButtonCancel = find.byValueKey('PromptButtonCancel');
final promptButtonOk = find.byValueKey('PromptButtonOk');
final sideMenuButton = find.byValueKey('SideMenu');
final listTiles = find.byValueKey('ListTiles');
final nextTest = find.byValueKey('InAppWebViewOnSafeBrowsingHitTest');
while((await driver.getText(appBarTitle)) == "InAppWebViewOnJsDialogTest") {
await Future.delayed(const Duration(milliseconds: 500));
}
await driver.tap(alertButtonOk);
String title = await driver.getText(appBarTitle);
expect(title, "alert");
await Future.delayed(const Duration(milliseconds: 500));
await driver.tap(confirmButtonOk);
title = await driver.getText(appBarTitle);
expect(title, "confirm true");
await Future.delayed(const Duration(milliseconds: 500));
await driver.tap(promptTextField);
await driver.enterText("new value");
await driver.waitFor(find.text("new value"));
await driver.tap(promptButtonOk);
title = await driver.getText(appBarTitle);
expect(title, "prompt new value");
await driver.tap(sideMenuButton);
await driver.scrollUntilVisible(listTiles, nextTest, dyScroll: -300.0);
await driver.tap(nextTest);
}, timeout: new Timeout(new Duration(minutes: 5)));
test('InAppWebViewOnSafeBrowsingHitTest', () async {
final appBarTitle = find.byValueKey('AppBarTitle');
final sideMenuButton = find.byValueKey('SideMenu');
final listTiles = find.byValueKey('ListTiles');
final nextTest = find.byValueKey('InAppWebViewOnReceivedHttpAuthRequestTest');
while((await driver.getText(appBarTitle)) == "InAppWebViewOnSafeBrowsingHitTest") {
await Future.delayed(const Duration(milliseconds: 500));
}
String url = await driver.getText(appBarTitle);
if (Platform.isAndroid)
expect(url, "chrome://safe-browsing/match?type=malware");
await driver.tap(sideMenuButton);
await driver.scrollUntilVisible(listTiles, nextTest, dyScroll: -300.0);
await driver.tap(nextTest);
}, timeout: new Timeout(new Duration(minutes: 5)));
test('InAppWebViewOnReceivedHttpAuthRequestTest', () async {
final appBarTitle = find.byValueKey('AppBarTitle');
final sideMenuButton = find.byValueKey('SideMenu');
final listTiles = find.byValueKey('ListTiles');
final nextTest = find.byValueKey('InAppWebViewSslRequestTest');
while((await driver.getText(appBarTitle)) == "InAppWebViewOnReceivedHttpAuthRequestTest") {
await Future.delayed(const Duration(milliseconds: 500));
}
String title = await driver.getText(appBarTitle);
expect(title, "Authorized");
await driver.tap(sideMenuButton);
await driver.scrollUntilVisible(listTiles, nextTest, dyScroll: -300.0);
await driver.tap(nextTest);
}, timeout: new Timeout(new Duration(minutes: 5)));
test('InAppWebViewSslRequestTest', () async {
final appBarTitle = find.byValueKey('AppBarTitle');
final sideMenuButton = find.byValueKey('SideMenu');
final listTiles = find.byValueKey('ListTiles');
final nextTest = find.byValueKey('InAppWebViewOnFindResultReceivedTest');
while((await driver.getText(appBarTitle)) == "InAppWebViewSslRequestTest") {
await Future.delayed(const Duration(milliseconds: 500));
}
String title = await driver.getText(appBarTitle);
expect(title, "Authorized");
await driver.tap(sideMenuButton);
await driver.scrollUntilVisible(listTiles, nextTest, dyScroll: -300.0);
await driver.tap(nextTest);
}, timeout: new Timeout(new Duration(minutes: 5)));
test('InAppWebViewOnFindResultReceivedTest', () async {
final appBarTitle = find.byValueKey('AppBarTitle');
final sideMenuButton = find.byValueKey('SideMenu');
final listTiles = find.byValueKey('ListTiles');
final nextTest = find.byValueKey('InAppWebViewOnNavigationStateChangeTest');
while((await driver.getText(appBarTitle)) == "InAppWebViewOnFindResultReceivedTest") {
await Future.delayed(const Duration(milliseconds: 500));
}
String title = await driver.getText(appBarTitle);
expect(title, "2");
await driver.tap(sideMenuButton);
await driver.scrollUntilVisible(listTiles, nextTest, dyScroll: -300.0);
await driver.tap(nextTest);
}, timeout: new Timeout(new Duration(minutes: 5)));
test('InAppWebViewOnNavigationStateChangeTest', () async {
final appBarTitle = find.byValueKey('AppBarTitle');
final sideMenuButton = find.byValueKey('SideMenu');
final listTiles = find.byValueKey('ListTiles');
final nextTest = find.byValueKey('InAppWebViewOnLoadErrorTest');
while((await driver.getText(appBarTitle)) == "InAppWebViewOnNavigationStateChangeTest") {
await Future.delayed(const Duration(milliseconds: 500));
}
String title = await driver.getText(appBarTitle);
expect(true, title.contains("first-push") && title.contains("second-push"));
await driver.tap(sideMenuButton);
await driver.scrollUntilVisible(listTiles, nextTest, dyScroll: -300.0);
await driver.tap(nextTest);
}, timeout: new Timeout(new Duration(minutes: 5)));
test('InAppWebViewOnLoadErrorTest', () async {
final appBarTitle = find.byValueKey('AppBarTitle');
final sideMenuButton = find.byValueKey('SideMenu');
final listTiles = find.byValueKey('ListTiles');
final nextTest = find.byValueKey('InAppWebViewOnLoadHttpErrorTest');
while((await driver.getText(appBarTitle)) == "InAppWebViewOnLoadErrorTest") {
await Future.delayed(const Duration(milliseconds: 500));
}
String title = await driver.getText(appBarTitle);
if (Platform.isAndroid) {
expect(title, "-2");
} else if (Platform.isIOS) {
expect(title, "-1022");
}
await driver.tap(sideMenuButton);
await driver.scrollUntilVisible(listTiles, nextTest, dyScroll: -300.0);
await driver.tap(nextTest);
}, timeout: new Timeout(new Duration(minutes: 5)));
test('InAppWebViewOnLoadHttpErrorTest', () async {
final appBarTitle = find.byValueKey('AppBarTitle');
final sideMenuButton = find.byValueKey('SideMenu');
final listTiles = find.byValueKey('ListTiles');
final nextTest = find.byValueKey('InAppWebViewCookieManagerTest');
while((await driver.getText(appBarTitle)) == "InAppWebViewOnLoadHttpErrorTest") {
await Future.delayed(const Duration(milliseconds: 500));
}
String title = await driver.getText(appBarTitle);
expect(title, "404");
await driver.tap(sideMenuButton);
await driver.scrollUntilVisible(listTiles, nextTest, dyScroll: -300.0);
await driver.tap(nextTest);
}, timeout: new Timeout(new Duration(minutes: 5)));
test('InAppWebViewCookieManagerTest', () async {
final appBarTitle = find.byValueKey('AppBarTitle');
final sideMenuButton = find.byValueKey('SideMenu');
final listTiles = find.byValueKey('ListTiles');
final nextTest = find.byValueKey('InAppWebViewHttpAuthCredentialDatabaseTest');
while((await driver.getText(appBarTitle)) == "InAppWebViewCookieManagerTest") {
await Future.delayed(const Duration(milliseconds: 500));
}
String title = await driver.getText(appBarTitle);
expect(title, "myValue true true");
await driver.tap(sideMenuButton);
await driver.scrollUntilVisible(listTiles, nextTest, dyScroll: -300.0);
await driver.tap(nextTest);
}, timeout: new Timeout(new Duration(minutes: 5)));
test('InAppWebViewHttpAuthCredentialDatabaseTest', () async {
final appBarTitle = find.byValueKey('AppBarTitle');
final sideMenuButton = find.byValueKey('SideMenu');
final listTiles = find.byValueKey('ListTiles');
final nextTest = find.byValueKey('InAppWebViewContentBlockerTest');
while((await driver.getText(appBarTitle)) == "InAppWebViewHttpAuthCredentialDatabaseTest") {
await Future.delayed(const Duration(milliseconds: 500));
}
String title = await driver.getText(appBarTitle);
expect(title, "Authorized true true");
await driver.tap(sideMenuButton);
await driver.scrollUntilVisible(listTiles, nextTest, dyScroll: -300.0);
await driver.tap(nextTest);
}, timeout: new Timeout(new Duration(minutes: 5)));
test('InAppWebViewContentBlockerTest', () async {
final appBarTitle = find.byValueKey('AppBarTitle');
while((await driver.getText(appBarTitle)) == "InAppWebViewContentBlockerTest") {
await Future.delayed(const Duration(milliseconds: 500));
}
String title = await driver.getText(appBarTitle);
expect(title, "true");
}, timeout: new Timeout(new Duration(minutes: 5)));
});
}

View File

@ -1,26 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
class WidgetTest extends StatefulWidget {
final WidgetTestState state = WidgetTestState();
WidgetTest({Key key}): super(key: key);
@override
WidgetTestState createState() {
return state;
}
}
class WidgetTestState extends State<WidgetTest> {
final GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey<ScaffoldState>();
InAppWebViewController webView;
String appBarTitle;
@override
Widget build(BuildContext context) {
return null;
}
}

View File

@ -1,112 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'main_test.dart';
import 'custom_widget_test.dart';
import '.env.dart';
class InAppWebViewAjaxTest extends WidgetTest {
final InAppWebViewAjaxTestState state = InAppWebViewAjaxTestState();
@override
InAppWebViewAjaxTestState createState() => state;
}
class InAppWebViewAjaxTestState extends WidgetTestState {
String appBarTitle = "InAppWebViewAjaxTest";
int totTests = 2;
int testsDone = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
key: this.scaffoldKey,
appBar: myAppBar(state: this, title: appBarTitle),
drawer: myDrawer(context: context),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialData: InAppWebViewInitialData(data: """
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>InAppWebViewAjaxTest</title>
</head>
<body>
<h1>InAppWebViewAjaxTest</h1>
<script>
window.addEventListener('flutterInAppWebViewPlatformReady', function(event) {
var xhttp = new XMLHttpRequest();
xhttp.open("POST", "http://${environment["NODE_SERVER_IP"]}:8082/test-ajax-post");
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhttp.send("firstname=Foo&lastname=Bar");
var xhttp2 = new XMLHttpRequest();
xhttp2.open("GET", "http://${environment["NODE_SERVER_IP"]}:8082/test-download-file");
xhttp2.send();
});
</script>
</body>
</html>
"""),
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true,
useShouldInterceptAjaxRequest: true,
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (InAppWebViewController controller, String url) {
},
onLoadStop: (InAppWebViewController controller, String url) {
},
shouldInterceptAjaxRequest: (InAppWebViewController controller, AjaxRequest ajaxRequest) async {
if (ajaxRequest.url.endsWith("/test-ajax-post")) {
ajaxRequest.responseType = 'json';
ajaxRequest.data = "firstname=Lorenzo&lastname=Pichilli";
}
return ajaxRequest;
},
onAjaxReadyStateChange: (InAppWebViewController controller, AjaxRequest ajaxRequest) async {
if (ajaxRequest.readyState == AjaxRequestReadyState.DONE && ajaxRequest.status == 200 && ajaxRequest.url.endsWith("/test-ajax-post")) {
Map<String, Object> res = ajaxRequest.response;
appBarTitle = (appBarTitle == "InAppWebViewAjaxTest") ? res['fullname'] : appBarTitle + " " + res['fullname'];
updateCountTest(context: context);
}
return AjaxRequestAction.PROCEED;
},
onAjaxProgress: (InAppWebViewController controller, AjaxRequest ajaxRequest) async {
if (ajaxRequest.event.type == AjaxRequestEventType.LOAD && ajaxRequest.url.endsWith("/test-ajax-post")) {
Map<String, Object> res = ajaxRequest.response;
appBarTitle = (appBarTitle == "InAppWebViewAjaxTest") ? res['fullname'] : appBarTitle + " " + res['fullname'];
updateCountTest(context: context);
}
return AjaxRequestAction.PROCEED;
},
),
),
),
])
)
);
}
void updateCountTest({@required BuildContext context}) {
testsDone++;
if (testsDone == totTests) {
setState(() { });
}
}
}

View File

@ -1,70 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'custom_widget_test.dart';
import 'main_test.dart';
class InAppWebViewContentBlockerTest extends WidgetTest {
final InAppWebViewContentBlockerTestState state = InAppWebViewContentBlockerTestState();
@override
InAppWebViewContentBlockerTestState createState() => state;
}
class InAppWebViewContentBlockerTestState extends WidgetTestState {
String appBarTitle = "InAppWebViewContentBlockerTest";
@override
Widget build(BuildContext context) {
return Scaffold(
key: this.scaffoldKey,
appBar: myAppBar(state: this, title: appBarTitle),
drawer: myDrawer(context: context),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialUrl: "https://flutter.dev/",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true,
contentBlockers: [
ContentBlocker(
trigger: ContentBlockerTrigger(
urlFilter: ".*",
resourceType: [
ContentBlockerTriggerResourceType.IMAGE,
ContentBlockerTriggerResourceType.STYLE_SHEET
],
ifTopUrl: [
"https://flutter.dev/"
]),
action: ContentBlockerAction(
type: ContentBlockerActionType.BLOCK))
]
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (InAppWebViewController controller, String url) {
},
onLoadStop: (InAppWebViewController controller, String url) {
setState(() {
appBarTitle = "true";
});
},
),
),
),
])
)
);
}
}

View File

@ -1,69 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'custom_widget_test.dart';
import 'main_test.dart';
class InAppWebViewCookieManagerTest extends WidgetTest {
final InAppWebViewCookieManagerTestState state = InAppWebViewCookieManagerTestState();
@override
InAppWebViewCookieManagerTestState createState() => state;
}
class InAppWebViewCookieManagerTestState extends WidgetTestState {
String appBarTitle = "InAppWebViewCookieManagerTest";
CookieManager cookieManager = CookieManager.instance();
@override
Widget build(BuildContext context) {
return Scaffold(
key: this.scaffoldKey,
appBar: myAppBar(state: this, title: appBarTitle),
drawer: myDrawer(context: context),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialUrl: "https://flutter.dev/",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (InAppWebViewController controller, String url) {
},
onLoadStop: (InAppWebViewController controller, String url) async {
var title = "";
await cookieManager.getCookies(url: url);
await cookieManager.setCookie(url: url, name: "myCookie", value: "myValue");
Cookie cookie = await cookieManager.getCookie(url: url, name: "myCookie");
title = cookie.value.toString();
await cookieManager.deleteCookie(url: url, name: "myCookie");
cookie = await cookieManager.getCookie(url: url, name: "myCookie");
title += " " + ((cookie == null) ? "true" : "false");
await cookieManager.deleteCookies(url: url);
List<Cookie> cookies = await cookieManager.getCookies(url: url);
title += " " + ((cookies.length == 0) ? "true" : "false");
setState(() {
appBarTitle = title;
});
},
),
),
),
])
)
);
}
}

View File

@ -1,127 +0,0 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'main_test.dart';
import 'custom_widget_test.dart';
import '.env.dart';
class InAppWebViewFetchTest extends WidgetTest {
final InAppWebViewFetchTestState state = InAppWebViewFetchTestState();
@override
InAppWebViewFetchTestState createState() => state;
}
class InAppWebViewFetchTestState extends WidgetTestState {
String appBarTitle = "InAppWebViewFetchTest";
int totTests = 2;
int testsDone = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
key: this.scaffoldKey,
appBar: myAppBar(state: this, title: appBarTitle),
drawer: myDrawer(context: context),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialData: InAppWebViewInitialData(data: """
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>InAppWebViewFetchTest</title>
</head>
<body>
<h1>InAppWebViewFetchTest</h1>
<script>
window.addEventListener('flutterInAppWebViewPlatformReady', function(event) {
fetch(new Request("http://${environment["NODE_SERVER_IP"]}:8082/test-download-file")).then(function(response) {
window.flutter_inappwebview.callHandler('fetchGet', response.status);
}).catch(function(error) {
window.flutter_inappwebview.callHandler('fetchGet', "ERROR: " + error);
});
fetch("http://${environment["NODE_SERVER_IP"]}:8082/test-ajax-post", {
method: 'POST',
body: JSON.stringify({
firstname: 'Foo',
lastname: 'Bar'
}),
headers: {
'Content-Type': 'application/json'
}
}).then(function(response) {
response.json().then(function(value) {
window.flutter_inappwebview.callHandler('fetchPost', value);
}).catch(function(error) {
window.flutter_inappwebview.callHandler('fetchPost', "ERROR: " + error);
});
}).catch(function(error) {
window.flutter_inappwebview.callHandler('fetchPost', "ERROR: " + error);
});
});
</script>
</body>
</html>
"""),
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true,
useShouldInterceptFetchRequest: true,
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
webView.addJavaScriptHandler(handlerName: "fetchGet", callback: (args) {
appBarTitle = (appBarTitle == "InAppWebViewFetchTest") ? args[0].toString() : appBarTitle + " " + args[0].toString();
updateCountTest(context: context);
});
webView.addJavaScriptHandler(handlerName: "fetchPost", callback: (args) {
appBarTitle = (appBarTitle == "InAppWebViewFetchTest") ? args[0]["fullname"] : appBarTitle + " " + args[0]["fullname"];
updateCountTest(context: context);
});
},
onLoadStart: (InAppWebViewController controller, String url) {
},
onLoadStop: (InAppWebViewController controller, String url) {
},
shouldInterceptFetchRequest: (InAppWebViewController controller, FetchRequest fetchRequest) async {
if (fetchRequest.url.endsWith("/test-ajax-post")) {
fetchRequest.body = utf8.encode("""{
"firstname": "Lorenzo",
"lastname": "Pichilli"
}
""");
}
return fetchRequest;
},
),
),
),
])
)
);
}
void updateCountTest({@required BuildContext context}) {
testsDone++;
if (testsDone == totTests) {
setState(() { });
}
}
}

View File

@ -1,79 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'custom_widget_test.dart';
import 'main_test.dart';
import '.env.dart';
class InAppWebViewHttpAuthCredentialDatabaseTest extends WidgetTest {
final InAppWebViewHttpAuthCredentialDatabaseTestState state = InAppWebViewHttpAuthCredentialDatabaseTestState();
@override
InAppWebViewHttpAuthCredentialDatabaseTestState createState() => state;
}
class InAppWebViewHttpAuthCredentialDatabaseTestState extends WidgetTestState {
String appBarTitle = "InAppWebViewHttpAuthCredentialDatabaseTest";
HttpAuthCredentialDatabase httpAuthCredentialDatabase = HttpAuthCredentialDatabase.instance();
@override
Widget build(BuildContext context) {
httpAuthCredentialDatabase.setHttpAuthCredential(
protectionSpace: ProtectionSpace(host: environment["NODE_SERVER_IP"], protocol: "http", realm: "Node", port: 8081),
credential: HttpAuthCredential(username: "USERNAME", password: "PASSWORD")
);
return Scaffold(
key: this.scaffoldKey,
appBar: myAppBar(state: this, title: appBarTitle),
drawer: myDrawer(context: context),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialUrl: "http://${environment["NODE_SERVER_IP"]}:8081/",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (InAppWebViewController controller, String url) {
},
onLoadStop: (InAppWebViewController controller, String url) async {
var title = "";
String h1Content = await controller.evaluateJavascript(source: "document.body.querySelector('h1').textContent");
title = h1Content;
var credentials = await httpAuthCredentialDatabase.getHttpAuthCredentials(protectionSpace:
ProtectionSpace(host: environment["NODE_SERVER_IP"], protocol: "http", realm: "Node", port: 8081)
);
title += " " + ((credentials.length == 1) ? "true" : "false");
await httpAuthCredentialDatabase.clearAllAuthCredentials();
credentials = await httpAuthCredentialDatabase.getHttpAuthCredentials(protectionSpace:
ProtectionSpace(host: environment["NODE_SERVER_IP"], protocol: "http", realm: "Node", port: 8081)
);
title += " " + ((credentials.length == 0) ? "true" : "false");
setState(() {
appBarTitle = title;
});
},
onReceivedHttpAuthRequest: (InAppWebViewController controller, HttpAuthChallenge challenge) async {
return new HttpAuthResponse(action: HttpAuthResponseAction.USE_SAVED_HTTP_AUTH_CREDENTIALS);
},
),
),
),
])
)
);
}
}

View File

@ -1,92 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'main_test.dart';
import 'custom_widget_test.dart';
class InAppWebViewInitialDataTest extends WidgetTest {
final InAppWebViewInitialDataTestState state = InAppWebViewInitialDataTestState();
@override
InAppWebViewInitialDataTestState createState() => state;
}
class InAppWebViewInitialDataTestState extends WidgetTestState {
String appBarTitle = "InAppWebViewInitialDataTest";
@override
Widget build(BuildContext context) {
return Scaffold(
key: this.scaffoldKey,
appBar: myAppBar(state: this, title: appBarTitle),
drawer: myDrawer(context: context),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialData: InAppWebViewInitialData(data: """
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>InAppWebViewInitialDataTest</title>
<link rel="stylesheet" href="https://getbootstrap.com/docs/4.3/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="css/style.css">
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<link rel="shortcut icon" href="favicon.ico">
</head>
<body class="text-center">
<div class="cover-container d-flex w-100 h-100 p-3 mx-auto flex-column">
<header class="masthead mb-auto">
<div class="inner">
<h3 class="masthead-brand">InAppWebViewInitialDataTest</h3>
<nav class="nav nav-masthead justify-content-center">
<a class="nav-link active" href="index.html">Home</a>
<a class="nav-link" href="page-1.html">Page 1</a>
<a class="nav-link" href="page-2.html">Page 2</a>
</nav>
</div>
</header>
<main role="main" class="inner cover">
<h1 class="cover-heading">InAppWebViewInitialFileTest</h1>
<img src="images/flutter-logo.svg" alt="flutter logo">
<p>
<img src="https://via.placeholder.com/100x50" alt="placeholder 100x50">
</p>
<a id="link" href="https://github.com/pichillilorenzo/flutter_inappwebview">flutter_inappwebview</a>
</main>
</div>
</body>
</html>
"""),
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (InAppWebViewController controller, String url) {
},
onLoadStop: (InAppWebViewController controller, String url) {
setState(() {
appBarTitle = "true";
});
},
),
),
),
])
)
);
}
}

View File

@ -1,55 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'main_test.dart';
import 'custom_widget_test.dart';
class InAppWebViewInitialFileTest extends WidgetTest {
final InAppWebViewInitialFileTestState state = InAppWebViewInitialFileTestState();
@override
InAppWebViewInitialFileTestState createState() => state;
}
class InAppWebViewInitialFileTestState extends WidgetTestState {
String appBarTitle = "InAppWebViewInitialFileTest";
@override
Widget build(BuildContext context) {
return Scaffold(
key: this.scaffoldKey,
appBar: myAppBar(state: this, title: appBarTitle),
drawer: myDrawer(context: context),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialFile: "test_assets/in_app_webview_initial_file_test.html",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (InAppWebViewController controller, String url) {
},
onLoadStop: (InAppWebViewController controller, String url) {
setState(() {
appBarTitle = "true";
});
},
),
),
),
])
)
);
}
}

View File

@ -1,56 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'custom_widget_test.dart';
import 'main_test.dart';
class InAppWebViewInitialUrlTest extends WidgetTest {
final InAppWebViewInitialUrlTestState state = InAppWebViewInitialUrlTestState();
@override
InAppWebViewInitialUrlTestState createState() => state;
}
class InAppWebViewInitialUrlTestState extends WidgetTestState {
String appBarTitle = "InAppWebViewInitialUrlTest";
@override
Widget build(BuildContext context) {
return Scaffold(
key: this.scaffoldKey,
appBar: myAppBar(state: this, title: appBarTitle),
drawer: myDrawer(context: context),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialUrl: "https://flutter.dev/",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (InAppWebViewController controller, String url) {
},
onLoadStop: (InAppWebViewController controller, String url) {
setState(() {
appBarTitle = url;
});
},
),
),
),
])
)
);
}
}

View File

@ -1,84 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'main_test.dart';
import 'custom_widget_test.dart';
class Foo {
String bar;
String baz;
Foo({this.bar, this.baz});
Map<String, dynamic> toJson() {
return {
'bar': this.bar,
'baz': this.baz
};
}
}
class InAppWebViewJavaScriptHandlerTest extends WidgetTest {
final InAppWebViewJavaScriptHandlerTestState state = InAppWebViewJavaScriptHandlerTestState();
@override
InAppWebViewJavaScriptHandlerTestState createState() => state;
}
class InAppWebViewJavaScriptHandlerTestState extends WidgetTestState {
String appBarTitle = "InAppWebViewJavaScriptHandlerTest";
@override
Widget build(BuildContext context) {
return Scaffold(
key: this.scaffoldKey,
appBar: myAppBar(state: this, title: appBarTitle),
drawer: myDrawer(context: context),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialFile: "test_assets/in_app_webview_javascript_handler_test.html",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
controller.addJavaScriptHandler(handlerName:'handlerFoo', callback: (args) {
appBarTitle = (args.length == 0).toString();
return Foo(bar: 'bar_value', baz: 'baz_value');
});
controller.addJavaScriptHandler(handlerName: 'handlerFooWithArgs', callback: (args) {
appBarTitle += " " + (args[0] is int).toString();
appBarTitle += " " + (args[1] is bool).toString();
appBarTitle += " " + (args[2] is List).toString();
appBarTitle += " " + (args[2] is List).toString();
appBarTitle += " " + (args[3] is Map).toString();
appBarTitle += " " + (args[4] is Map).toString();
setState(() { });
});
},
onLoadStart: (InAppWebViewController controller, String url) {
},
onLoadStop: (InAppWebViewController controller, String url) {
},
),
),
),
])
)
);
}
}

View File

@ -1,58 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'main_test.dart';
import 'custom_widget_test.dart';
class InAppWebViewOnConsoleMessageTest extends WidgetTest {
final InAppWebViewOnConsoleMessageTestState state = InAppWebViewOnConsoleMessageTestState();
@override
InAppWebViewOnConsoleMessageTestState createState() => state;
}
class InAppWebViewOnConsoleMessageTestState extends WidgetTestState {
String appBarTitle = "InAppWebViewOnConsoleMessageTest";
@override
Widget build(BuildContext context) {
return Scaffold(
key: this.scaffoldKey,
appBar: myAppBar(state: this, title: appBarTitle),
drawer: myDrawer(context: context),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialFile: "test_assets/in_app_webview_on_console_message_test.html",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (InAppWebViewController controller, String url) {
},
onLoadStop: (InAppWebViewController controller, String url) {
},
onConsoleMessage: (InAppWebViewController controller, ConsoleMessage consoleMessage) {
setState(() {
appBarTitle = consoleMessage.message + " " + consoleMessage.messageLevel.toString();
});
},
),
),
),
])
)
);
}
}

View File

@ -1,62 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'main_test.dart';
import 'custom_widget_test.dart';
class InAppWebViewOnCreateWindowTest extends WidgetTest {
final InAppWebViewOnCreateWindowTestState state = InAppWebViewOnCreateWindowTestState();
@override
InAppWebViewOnCreateWindowTestState createState() => state;
}
class InAppWebViewOnCreateWindowTestState extends WidgetTestState {
String appBarTitle = "InAppWebViewOnCreateWindowTest";
@override
Widget build(BuildContext context) {
return Scaffold(
key: this.scaffoldKey,
appBar: myAppBar(state: this, title: appBarTitle),
drawer: myDrawer(context: context),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialFile: "test_assets/in_app_webview_on_create_window_test.html",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true,
javaScriptCanOpenWindowsAutomatically: true,
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (InAppWebViewController controller, String url) {
},
onLoadStop: (InAppWebViewController controller, String url) {
if (url == "https://flutter.dev/") {
setState(() {
appBarTitle = url;
});
}
},
onCreateWindow: (InAppWebViewController controller, CreateWindowRequest createWindowRequest) async {
controller.loadUrl(url: createWindowRequest.url);
return null;
},
),
),
),
])
)
);
}
}

View File

@ -1,79 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'main_test.dart';
import 'custom_widget_test.dart';
import '.env.dart';
class InAppWebViewOnDownloadStartTest extends WidgetTest {
final InAppWebViewOnDownloadStartTestState state = InAppWebViewOnDownloadStartTestState();
@override
InAppWebViewOnDownloadStartTestState createState() => state;
}
class InAppWebViewOnDownloadStartTestState extends WidgetTestState {
String appBarTitle = "InAppWebViewOnDownloadStartTest";
@override
Widget build(BuildContext context) {
return Scaffold(
key: this.scaffoldKey,
appBar: myAppBar(state: this, title: appBarTitle),
drawer: myDrawer(context: context),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialData: InAppWebViewInitialData(data: """
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>InAppWebViewOnDownloadStartTest</title>
</head>
<body>
<h1>InAppWebViewOnDownloadStartTest</h1>
<a id="download-file" href="http://${environment["NODE_SERVER_IP"]}:8082/test-download-file">download file</a>
<script>
window.addEventListener("flutterInAppWebViewPlatformReady", function(event) {
document.querySelector("#download-file").click();
});
</script>
</body>
</html>
"""),
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true,
useOnDownloadStart: true
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (InAppWebViewController controller, String url) {
},
onLoadStop: (InAppWebViewController controller, String url) {
},
onDownloadStart: (InAppWebViewController controller, String url) {
setState(() {
appBarTitle = url;
});
},
),
),
),
])
)
);
}
}

View File

@ -1,60 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'main_test.dart';
import 'custom_widget_test.dart';
class InAppWebViewOnFindResultReceivedTest extends WidgetTest {
final InAppWebViewOnFindResultReceivedTestState state = InAppWebViewOnFindResultReceivedTestState();
@override
InAppWebViewOnFindResultReceivedTestState createState() => state;
}
class InAppWebViewOnFindResultReceivedTestState extends WidgetTestState {
String appBarTitle = "InAppWebViewOnFindResultReceivedTest";
@override
Widget build(BuildContext context) {
return Scaffold(
key: this.scaffoldKey,
appBar: myAppBar(state: this, title: appBarTitle),
drawer: myDrawer(context: context),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialFile: "test_assets/in_app_webview_initial_file_test.html",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (InAppWebViewController controller, String url) {
},
onLoadStop: (InAppWebViewController controller, String url) {
controller.findAllAsync(find: "InAppWebViewInitialFileTest");
},
onFindResultReceived: (InAppWebViewController controller, int activeMatchOrdinal, int numberOfMatches, bool isDoneCounting) async {
if (isDoneCounting) {
setState(() {
appBarTitle = numberOfMatches.toString();
});
}
},
),
),
),
])
)
);
}
}

View File

@ -1,199 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'main_test.dart';
import 'custom_widget_test.dart';
class InAppWebViewOnJsDialogTest extends WidgetTest {
final InAppWebViewOnJsDialogTestState state = InAppWebViewOnJsDialogTestState();
@override
InAppWebViewOnJsDialogTestState createState() => state;
}
class InAppWebViewOnJsDialogTestState extends WidgetTestState {
String appBarTitle = "InAppWebViewOnJsDialogTest";
TextEditingController _textFieldController = TextEditingController();
@override
void dispose() {
super.dispose();
_textFieldController.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
key: this.scaffoldKey,
appBar: myAppBar(state: this, title: appBarTitle),
drawer: myDrawer(context: context),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialFile: "test_assets/in_app_webview_on_js_dialog_test.html",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
controller.addJavaScriptHandler(handlerName: 'confirm', callback: (args) {
setState(() {
appBarTitle = "confirm " + ((args[0] is bool && args[0]) ? "true" : "false");
});
});
controller.addJavaScriptHandler(handlerName: 'prompt', callback: (args) {
setState(() {
appBarTitle = "prompt " + args[0];
});
});
},
onLoadStart: (InAppWebViewController controller, String url) {
},
onLoadStop: (InAppWebViewController controller, String url) {
setState(() {
appBarTitle = "loaded";
});
},
onJsAlert:
(InAppWebViewController controller, JsAlertRequest jsAlertRequest) async {
JsAlertResponseAction action =
await createAlertDialog(context, jsAlertRequest.message);
return JsAlertResponse(
handledByClient: true, action: action);
},
onJsConfirm:
(InAppWebViewController controller, JsConfirmRequest jsConfirmRequest) async {
JsConfirmResponseAction action =
await createConfirmDialog(context, jsConfirmRequest.message);
return JsConfirmResponse(
handledByClient: true, action: action);
},
onJsPrompt: (InAppWebViewController controller, JsPromptRequest jsPromptRequest) async {
_textFieldController.text = jsPromptRequest.defaultValue;
JsPromptResponseAction action =
await createPromptDialog(context, jsPromptRequest.message);
return JsPromptResponse(
handledByClient: true,
action: action,
value: _textFieldController.text);
},
),
),
),
])
)
);
}
Future<JsAlertResponseAction> createAlertDialog(
BuildContext context, String message) async {
JsAlertResponseAction action;
await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: Text(message),
actions: <Widget>[
FlatButton(
child: Text("Ok"),
key: Key("AlertButtonOk"),
onPressed: () {
action = JsAlertResponseAction.CONFIRM;
Navigator.of(context).pop();
setState(() {
appBarTitle = "alert";
});
},
),
],
);
},
);
return action;
}
Future<JsConfirmResponseAction> createConfirmDialog(
BuildContext context, String message) async {
JsConfirmResponseAction action;
await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: Text(message),
actions: <Widget>[
FlatButton(
child: Text("Cancel"),
key: Key("ConfirmButtonCancel"),
onPressed: () {
action = JsConfirmResponseAction.CANCEL;
Navigator.of(context).pop();
},
),
FlatButton(
child: Text("Ok"),
key: Key("ConfirmButtonOk"),
onPressed: () {
action = JsConfirmResponseAction.CONFIRM;
Navigator.of(context).pop();
},
),
],
);
},
);
return action;
}
Future<JsPromptResponseAction> createPromptDialog(
BuildContext context, String message) async {
JsPromptResponseAction action;
await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(message),
content: TextField(
key: Key("PromptTextField"),
controller: _textFieldController,
),
actions: <Widget>[
FlatButton(
child: Text("Cancel"),
key: Key("PromptButtonCancel"),
onPressed: () {
action = JsPromptResponseAction.CANCEL;
Navigator.of(context).pop();
},
),
FlatButton(
child: Text("Ok"),
key: Key("PromptButtonOk"),
onPressed: () {
action = JsPromptResponseAction.CONFIRM;
Navigator.of(context).pop();
},
),
],
);
},
);
return action;
}
}

View File

@ -1,59 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'custom_widget_test.dart';
import 'main_test.dart';
class InAppWebViewOnLoadErrorTest extends WidgetTest {
final InAppWebViewOnLoadErrorTestState state = InAppWebViewOnLoadErrorTestState();
@override
InAppWebViewOnLoadErrorTestState createState() => state;
}
class InAppWebViewOnLoadErrorTestState extends WidgetTestState {
String appBarTitle = "InAppWebViewOnLoadErrorTest";
@override
Widget build(BuildContext context) {
return Scaffold(
key: this.scaffoldKey,
appBar: myAppBar(state: this, title: appBarTitle),
drawer: myDrawer(context: context),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialUrl: "https://not-existing-domain.org/",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (InAppWebViewController controller, String url) {
},
onLoadStop: (InAppWebViewController controller, String url) {
},
onLoadError: (InAppWebViewController controller, String url, int code, String message) async {
setState(() {
appBarTitle = code.toString();
});
}
),
),
),
])
)
);
}
}

View File

@ -1,59 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'custom_widget_test.dart';
import 'main_test.dart';
class InAppWebViewOnLoadHttpErrorTest extends WidgetTest {
final InAppWebViewOnLoadHttpErrorTestState state = InAppWebViewOnLoadHttpErrorTestState();
@override
InAppWebViewOnLoadHttpErrorTestState createState() => state;
}
class InAppWebViewOnLoadHttpErrorTestState extends WidgetTestState {
String appBarTitle = "InAppWebViewOnLoadHttpErrorTest";
@override
Widget build(BuildContext context) {
return Scaffold(
key: this.scaffoldKey,
appBar: myAppBar(state: this, title: appBarTitle),
drawer: myDrawer(context: context),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialUrl: "https://google.com/404",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (InAppWebViewController controller, String url) {
},
onLoadStop: (InAppWebViewController controller, String url) {
},
onLoadHttpError: (InAppWebViewController controller, String url, int statusCode, String description) async {
setState(() {
appBarTitle = statusCode.toString();
});
},
),
),
),
])
)
);
}
}

View File

@ -1,69 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'main_test.dart';
import 'custom_widget_test.dart';
class InAppWebViewOnLoadResourceCustomSchemeTest extends WidgetTest {
final InAppWebViewOnLoadResourceCustomSchemeTestState state = InAppWebViewOnLoadResourceCustomSchemeTestState();
@override
InAppWebViewOnLoadResourceCustomSchemeTestState createState() => state;
}
class InAppWebViewOnLoadResourceCustomSchemeTestState extends WidgetTestState {
String appBarTitle = "InAppWebViewOnLoadResourceCustomSchemeTest";
@override
Widget build(BuildContext context) {
return Scaffold(
key: this.scaffoldKey,
appBar: myAppBar(state: this, title: appBarTitle),
drawer: myDrawer(context: context),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialFile: "test_assets/in_app_webview_on_load_resource_custom_scheme_test.html",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true,
resourceCustomSchemes: ["my-special-custom-scheme"]
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
webView.addJavaScriptHandler(handlerName: "imageLoaded", callback: (args) {
setState(() {
appBarTitle = "true";
});
});
},
onLoadStart: (InAppWebViewController controller, String url) {
},
onLoadStop: (InAppWebViewController controller, String url) {
},
onLoadResourceCustomScheme: (InAppWebViewController controller, String scheme, String url) async {
if (scheme == "my-special-custom-scheme") {
var bytes = await rootBundle.load("test_assets/" + url.replaceFirst("my-special-custom-scheme://", "", 0));
var response = CustomSchemeResponse(data: bytes.buffer.asUint8List(), contentType: "image/svg+xml", contentEnconding: "utf-8");
return response;
}
return null;
},
),
),
),
])
)
);
}
}

View File

@ -1,67 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'main_test.dart';
import 'custom_widget_test.dart';
class InAppWebViewOnLoadResourceTest extends WidgetTest {
final InAppWebViewOnLoadResourceTestState state = InAppWebViewOnLoadResourceTestState();
@override
InAppWebViewOnLoadResourceTestState createState() => state;
}
class InAppWebViewOnLoadResourceTestState extends WidgetTestState {
List<String> resourceList = [
"https://getbootstrap.com/docs/4.3/dist/css/bootstrap.min.css",
"https://code.jquery.com/jquery-3.3.1.min.js",
"https://via.placeholder.com/100x50"
];
int countResources = 0;
String appBarTitle = "InAppWebViewOnLoadResourceTest";
@override
Widget build(BuildContext context) {
return Scaffold(
key: this.scaffoldKey,
appBar: myAppBar(state: this, title: appBarTitle),
drawer: myDrawer(context: context),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialFile: "test_assets/in_app_webview_on_load_resource_test.html",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true,
useOnLoadResource: true
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (InAppWebViewController controller, String url) {
},
onLoadStop: (InAppWebViewController controller, String url) {
},
onLoadResource: (InAppWebViewController controller, LoadedResource response) {
appBarTitle = (appBarTitle == "InAppWebViewOnLoadResourceTest") ? response.url : appBarTitle + " " + response.url;
countResources++;
if (countResources == resourceList.length) {
setState(() { });
}
}
),
),
),
])
)
);
}
}

View File

@ -1,72 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'main_test.dart';
import 'custom_widget_test.dart';
class InAppWebViewOnNavigationStateChangeTest extends WidgetTest {
final InAppWebViewOnNavigationStateChangeTestState state = InAppWebViewOnNavigationStateChangeTestState();
@override
InAppWebViewOnNavigationStateChangeTestState createState() => state;
}
class InAppWebViewOnNavigationStateChangeTestState extends WidgetTestState {
String appBarTitle = "InAppWebViewOnNavigationStateChangeTest";
@override
Widget build(BuildContext context) {
return Scaffold(
key: this.scaffoldKey,
appBar: myAppBar(state: this, title: appBarTitle),
drawer: myDrawer(context: context),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialUrl: "https://flutter.dev/",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (InAppWebViewController controller, String url) {
},
onLoadStop: (InAppWebViewController controller, String url) {
controller.evaluateJavascript(source: """
var state = {}
var title = ''
var url = 'first-push';
history.pushState(state, title, url);
setTimeout(function() {
var url = 'second-push';
history.pushState(state, title, url);
}, 100);
""");
},
onUpdateVisitedHistory: (InAppWebViewController controller, String url, bool androidIsReload) async {
if (url.endsWith("second-push")) {
setState(() {
appBarTitle += " " + url;
});
} else {
appBarTitle = url;
}
},
),
),
),
])
)
);
}
}

View File

@ -1,61 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'custom_widget_test.dart';
import 'main_test.dart';
class InAppWebViewOnProgressChangedTest extends WidgetTest {
final InAppWebViewOnProgressChangedTestState state = InAppWebViewOnProgressChangedTestState();
@override
InAppWebViewOnProgressChangedTestState createState() => state;
}
class InAppWebViewOnProgressChangedTestState extends WidgetTestState {
String appBarTitle = "InAppWebViewOnProgressChangedTest";
@override
Widget build(BuildContext context) {
return Scaffold(
key: this.scaffoldKey,
appBar: myAppBar(state: this, title: appBarTitle),
drawer: myDrawer(context: context),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialUrl: "https://flutter.dev/",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (InAppWebViewController controller, String url) {
},
onLoadStop: (InAppWebViewController controller, String url) {
},
onProgressChanged: (InAppWebViewController controller, int progress) {
if (progress == 100) {
setState(() {
appBarTitle = "true";
});
}
},
),
),
),
])
)
);
}
}

View File

@ -1,65 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'custom_widget_test.dart';
import 'main_test.dart';
import '.env.dart';
class InAppWebViewOnReceivedHttpAuthRequestTest extends WidgetTest {
final InAppWebViewOnReceivedHttpAuthRequestTestState state = InAppWebViewOnReceivedHttpAuthRequestTestState();
@override
InAppWebViewOnReceivedHttpAuthRequestTestState createState() => state;
}
class InAppWebViewOnReceivedHttpAuthRequestTestState extends WidgetTestState {
String appBarTitle = "InAppWebViewOnReceivedHttpAuthRequestTest";
@override
Widget build(BuildContext context) {
return Scaffold(
key: this.scaffoldKey,
appBar: myAppBar(state: this, title: appBarTitle),
drawer: myDrawer(context: context),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialUrl: "http://${environment["NODE_SERVER_IP"]}:8081/",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (InAppWebViewController controller, String url) {
},
onLoadStop: (InAppWebViewController controller, String url) async {
String h1Content = await controller.evaluateJavascript(source: "document.body.querySelector('h1').textContent");
setState(() {
appBarTitle = h1Content;
});
},
onReceivedHttpAuthRequest: (InAppWebViewController controller, HttpAuthChallenge challenge) async {
return new HttpAuthResponse(
username: "USERNAME",
password: "PASSWORD",
action: HttpAuthResponseAction.PROCEED,
permanentPersistence: true);
},
),
),
),
])
)
);
}
}

View File

@ -1,68 +0,0 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'custom_widget_test.dart';
import 'main_test.dart';
class InAppWebViewOnSafeBrowsingHitTest extends WidgetTest {
final InAppWebViewOnSafeBrowsingHitTestState state = InAppWebViewOnSafeBrowsingHitTestState();
@override
InAppWebViewOnSafeBrowsingHitTestState createState() => state;
}
class InAppWebViewOnSafeBrowsingHitTestState extends WidgetTestState {
String appBarTitle = "InAppWebViewOnSafeBrowsingHitTest";
@override
Widget build(BuildContext context) {
return Scaffold(
key: this.scaffoldKey,
appBar: myAppBar(state: this, title: appBarTitle),
drawer: myDrawer(context: context),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialUrl: (Platform.isAndroid) ? "chrome://safe-browsing/match?type=malware" : "https://flutter.dev/",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
// if I set javaScriptEnabled to true, it will crash!
javaScriptEnabled: false,
clearCache: true,
debuggingEnabled: true
),
android: AndroidInAppWebViewOptions(
safeBrowsingEnabled: true,
),
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
if(Platform.isAndroid)
controller.android.startSafeBrowsing();
},
onLoadStart: (InAppWebViewController controller, String url) {
},
onLoadStop: (InAppWebViewController controller, String url) {
setState(() {
appBarTitle = url;
});
},
androidOnSafeBrowsingHit: (InAppWebViewController controller, String url, SafeBrowsingThreat threatType) async {
return SafeBrowsingResponse(report: true, action: SafeBrowsingResponseAction.PROCEED);
},
),
),
),
])
)
);
}
}

View File

@ -1,63 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'custom_widget_test.dart';
import 'main_test.dart';
class InAppWebViewOnScrollChangedTest extends WidgetTest {
final InAppWebViewOnScrollChangedTestState state = InAppWebViewOnScrollChangedTestState();
@override
InAppWebViewOnScrollChangedTestState createState() => state;
}
class InAppWebViewOnScrollChangedTestState extends WidgetTestState {
String appBarTitle = "InAppWebViewOnScrollChangedTest";
bool scrolled = false;
@override
Widget build(BuildContext context) {
return Scaffold(
key: this.scaffoldKey,
appBar: myAppBar(state: this, title: appBarTitle),
drawer: myDrawer(context: context),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialUrl: "https://flutter.dev/",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (InAppWebViewController controller, String url) {
},
onLoadStop: (InAppWebViewController controller, String url) {
controller.scrollTo(x: 0, y: 500);
},
onScrollChanged: (InAppWebViewController controller, int x, int y) {
if (!scrolled) {
scrolled = true;
setState(() {
appBarTitle = "true";
});
}
},
),
),
),
])
)
);
}
}

View File

@ -1,67 +0,0 @@
import 'package:flutter/material.dart';
import 'dart:io' show Platform;
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'custom_widget_test.dart';
import 'main_test.dart';
class InAppWebViewShouldOverrideUrlLoadingTest extends WidgetTest {
final InAppWebViewShouldOverrideUrlLoadingTestState state = InAppWebViewShouldOverrideUrlLoadingTestState();
@override
InAppWebViewShouldOverrideUrlLoadingTestState createState() => state;
}
class InAppWebViewShouldOverrideUrlLoadingTestState extends WidgetTestState {
String appBarTitle = "InAppWebViewShouldOverrideUrlLoadingTest";
@override
Widget build(BuildContext context) {
return Scaffold(
key: this.scaffoldKey,
appBar: myAppBar(state: this, title: appBarTitle),
drawer: myDrawer(context: context),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialFile: "test_assets/in_app_webview_initial_file_test.html",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true,
useShouldOverrideUrlLoading: true
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (InAppWebViewController controller, String url) {
},
onLoadStop: (InAppWebViewController controller, String url) {
if (url == "https://flutter.dev/") {
setState(() {
appBarTitle = url;
});
} else {
controller.evaluateJavascript(source: "document.querySelector('#link').click();");
}
},
shouldOverrideUrlLoading: (InAppWebViewController controller, ShouldOverrideUrlLoadingRequest shouldOverrideUrlLoadingRequest) async {
if (Platform.isAndroid || shouldOverrideUrlLoadingRequest.iosWKNavigationType == IOSWKNavigationType.LINK_ACTIVATED) {
await controller.loadUrl(url: "https://flutter.dev/");
return ShouldOverrideUrlLoadingAction.CANCEL;
}
return ShouldOverrideUrlLoadingAction.ALLOW;
},
),
),
),
])
)
);
}
}

View File

@ -1,68 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'custom_widget_test.dart';
import 'main_test.dart';
import '.env.dart';
class InAppWebViewSslRequestTest extends WidgetTest {
final InAppWebViewSslRequestTestState state = InAppWebViewSslRequestTestState();
@override
InAppWebViewSslRequestTestState createState() => state;
}
class InAppWebViewSslRequestTestState extends WidgetTestState {
String appBarTitle = "InAppWebViewSslRequestTest";
@override
Widget build(BuildContext context) {
return Scaffold(
key: this.scaffoldKey,
appBar: myAppBar(state: this, title: appBarTitle),
drawer: myDrawer(context: context),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialUrl: "https://${environment["NODE_SERVER_IP"]}:4433/",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (InAppWebViewController controller, String url) {
},
onLoadStop: (InAppWebViewController controller, String url) async {
String h1Content = await controller.evaluateJavascript(source: "document.body.querySelector('h1').textContent");
setState(() {
appBarTitle = h1Content;
});
},
onReceivedServerTrustAuthRequest: (InAppWebViewController controller, ServerTrustChallenge challenge) async {
return new ServerTrustAuthResponse(action: ServerTrustAuthResponseAction.PROCEED);
},
onReceivedClientCertRequest: (InAppWebViewController controller, ClientCertChallenge challenge) async {
return new ClientCertResponse(
certificatePath: "test_assets/certificate.pfx",
certificatePassword: "",
androidKeyStoreType: "PKCS12",
action: ClientCertResponseAction.PROCEED);
},
),
),
),
])
)
);
}
}

View File

@ -0,0 +1,15 @@
// @dart = 2.9
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter_driver/flutter_driver.dart';
Future<void> main() async {
final FlutterDriver driver = await FlutterDriver.connect();
final String data =
await driver.requestData(null, timeout: const Duration(minutes: 1));
await driver.close();
final Map<String, dynamic> result = jsonDecode(data);
exit(result['result'] == 'true' ? 0 : 1);
}

View File

@ -1,153 +0,0 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'custom_widget_test.dart';
import 'in_app_webview_ajax_test.dart';
import 'in_app_webview_content_blocker_test.dart';
import 'in_app_webview_cookie_manager_test.dart';
import 'in_app_webview_fetch_test.dart';
import 'in_app_webview_http_auth_credential_database_test.dart';
import 'in_app_webview_initial_data_test.dart';
import 'in_app_webview_initial_file_test.dart';
import 'in_app_webview_initial_url_test.dart';
import 'in_app_webview_javascript_handler_test.dart';
import 'in_app_webview_on_console_message_test.dart';
import 'in_app_webview_on_download_start_test.dart';
import 'in_app_webview_on_find_result_received_test.dart';
import 'in_app_webview_on_js_dialog_test.dart';
import 'in_app_webview_on_load_error_test.dart';
import 'in_app_webview_on_load_http_error_test.dart';
import 'in_app_webview_on_load_resource_custom_scheme_test.dart';
import 'in_app_webview_on_load_resource_test.dart';
import 'in_app_webview_on_navigation_state_change_test.dart';
import 'in_app_webview_on_progress_changed_test.dart';
import 'in_app_webview_on_received_http_auth_request_test.dart';
import 'in_app_webview_on_safe_browsing_hit_test.dart';
import 'in_app_webview_on_scroll_changed_test.dart';
import 'in_app_webview_on_create_window_test.dart';
import 'in_app_webview_should_override_url_loading_test.dart';
import 'in_app_webview_ssl_request_test.dart';
Map<String, WidgetBuilder> getTestRoutes({@required BuildContext context}) {
var routes = {
'/': (context) => InAppWebViewInitialUrlTest(),
'/InAppWebViewInitialFileTest': (context) => InAppWebViewInitialFileTest(),
'/InAppWebViewInitialDataTest': (context) => InAppWebViewInitialDataTest(),
'/InAppWebViewOnProgressChangedTest': (context) => InAppWebViewOnProgressChangedTest(),
'/InAppWebViewOnScrollChangedTest': (context) => InAppWebViewOnScrollChangedTest(),
'/InAppWebViewOnLoadResourceTest': (context) => InAppWebViewOnLoadResourceTest(),
'/InAppWebViewJavaScriptHandlerTest': (context) => InAppWebViewJavaScriptHandlerTest(),
'/InAppWebViewAjaxTest': (context) => InAppWebViewAjaxTest(),
'/InAppWebViewFetchTest': (context) => InAppWebViewFetchTest(),
'/InAppWebViewOnLoadResourceCustomSchemeTest': (context) => InAppWebViewOnLoadResourceCustomSchemeTest(),
'/InAppWebViewShouldOverrideUrlLoadingTest': (context) => InAppWebViewShouldOverrideUrlLoadingTest(),
'/InAppWebViewOnConsoleMessageTest': (context) => InAppWebViewOnConsoleMessageTest(),
'/InAppWebViewOnDownloadStartTest': (context) => InAppWebViewOnDownloadStartTest(),
'/InAppWebViewOnCreateWindowTest': (context) => InAppWebViewOnCreateWindowTest(),
'/InAppWebViewOnJsDialogTest': (context) => InAppWebViewOnJsDialogTest(),
'/InAppWebViewOnSafeBrowsingHitTest': (context) => InAppWebViewOnSafeBrowsingHitTest(),
'/InAppWebViewOnReceivedHttpAuthRequestTest': (context) => InAppWebViewOnReceivedHttpAuthRequestTest(),
'/InAppWebViewSslRequestTest': (context) => InAppWebViewSslRequestTest(),
'/InAppWebViewOnFindResultReceivedTest': (context) => InAppWebViewOnFindResultReceivedTest(),
'/InAppWebViewOnNavigationStateChangeTest': (context) => InAppWebViewOnNavigationStateChangeTest(),
'/InAppWebViewOnLoadErrorTest': (context) => InAppWebViewOnLoadErrorTest(),
'/InAppWebViewOnLoadHttpErrorTest': (context) => InAppWebViewOnLoadHttpErrorTest(),
'/InAppWebViewCookieManagerTest': (context) => InAppWebViewCookieManagerTest(),
'/InAppWebViewHttpAuthCredentialDatabaseTest': (context) => InAppWebViewHttpAuthCredentialDatabaseTest(),
'/InAppWebViewContentBlockerTest': (context) => InAppWebViewContentBlockerTest(),
};
return routes;
}
AppBar myAppBar({@required WidgetTestState state, @required String title}) {
return AppBar(
title: Text(
title,
key: Key("AppBarTitle")
),
actions: <Widget>[
IconButton(
icon: Icon(Icons.menu),
key: Key("SideMenu"),
onPressed: () {
state.scaffoldKey.currentState.openDrawer();
},
),
IconButton(
icon: Icon(Icons.refresh),
onPressed: () {
if (state.webView != null)
state.webView.reload();
},
),
],
);
}
Drawer myDrawer({@required context}) {
var routes = getTestRoutes(context: context);
List<Widget> listTiles = [
DrawerHeader(
child: Text('Tests'),
decoration: BoxDecoration(
color: Colors.blue,
),
)
];
for (String k in routes.keys) {
var title = "";
if (k == "/") {
title = "InAppWebViewInitialUrlTest";
} else {
title = k.substring(1);
}
listTiles.add(ListTile(
title: Text(title),
key: Key(title),
onTap: () {
Navigator.pushReplacementNamed(context, k);
},
));
}
return Drawer(
child: ListView(
key: Key("ListTiles"),
padding: EdgeInsets.zero,
children: listTiles,
),
);
}
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(new MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'flutter_inappbrowser tests',
initialRoute: '/',
routes: getTestRoutes(context: context)
);
}
}

View File

@ -14,6 +14,12 @@
<excludeFolder url="file://$MODULE_DIR$/example/flutter_plugin/.dart_tool" /> <excludeFolder url="file://$MODULE_DIR$/example/flutter_plugin/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/flutter_plugin/.pub" /> <excludeFolder url="file://$MODULE_DIR$/example/flutter_plugin/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/flutter_plugin/build" /> <excludeFolder url="file://$MODULE_DIR$/example/flutter_plugin/build" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_downloader/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_downloader/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_downloader/build" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_downloader/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_downloader/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_downloader/example/build" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappbrowser/.dart_tool" /> <excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappbrowser/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappbrowser/.pub" /> <excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappbrowser/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappbrowser/build" /> <excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappbrowser/build" />
@ -27,6 +33,33 @@
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappwebview/example/.pub" /> <excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappwebview/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappwebview/example/build" /> <excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappwebview/example/build" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappwebview/example/ios/Flutter/App.framework/flutter_assets/packages" /> <excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappwebview/example/ios/Flutter/App.framework/flutter_assets/packages" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/integration_test/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/integration_test/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/integration_test/build" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/integration_test/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/integration_test/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/integration_test/example/build" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/integration_test/integration_test_macos/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/integration_test/integration_test_macos/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/integration_test/integration_test_macos/build" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/path_provider/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/path_provider/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/path_provider/build" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/path_provider/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/path_provider/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/path_provider/example/build" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/permission_handler/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/permission_handler/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/permission_handler/build" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/permission_handler/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/permission_handler/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/permission_handler/example/build" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/url_launcher/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/url_launcher/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/url_launcher/build" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/url_launcher/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/url_launcher/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/url_launcher/example/build" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/Flutter/App.framework/flutter_assets/packages" /> <excludeFolder url="file://$MODULE_DIR$/example/ios/Flutter/App.framework/flutter_assets/packages" />
<excludeFolder url="file://$MODULE_DIR$/flutter_inappbrowser_tests/.dart_tool" /> <excludeFolder url="file://$MODULE_DIR$/flutter_inappbrowser_tests/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/flutter_inappbrowser_tests/.pub" /> <excludeFolder url="file://$MODULE_DIR$/flutter_inappbrowser_tests/.pub" />

View File

@ -65,7 +65,9 @@ public class ChromeSafariBrowserManager: NSObject, FlutterPlugin {
if #available(iOS 9.0, *) { if #available(iOS 9.0, *) {
if let flutterViewController = UIApplication.shared.delegate?.window.unsafelyUnwrapped?.rootViewController as? FlutterViewController { if let flutterViewController = UIApplication.shared.delegate?.window.unsafelyUnwrapped?.rootViewController {
// flutterViewController could be casted to FlutterViewController if needed
let safariOptions = SafariBrowserOptions() let safariOptions = SafariBrowserOptions()
let _ = safariOptions.parse(options: options) let _ = safariOptions.parse(options: options)

View File

@ -1153,7 +1153,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
scrollView.maximumZoomScale = CGFloat(options.maximumZoomScale) scrollView.maximumZoomScale = CGFloat(options.maximumZoomScale)
scrollView.minimumZoomScale = CGFloat(options.minimumZoomScale) scrollView.minimumZoomScale = CGFloat(options.minimumZoomScale)
// options.debuggingEnabled is always enabled for iOS, // debugging is always enabled for iOS,
// there isn't any option to set about it such as on Android. // there isn't any option to set about it such as on Android.
if options.clearCache { if options.clearCache {
@ -1352,6 +1352,14 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
configuration.suppressesIncrementalRendering = options.suppressesIncrementalRendering configuration.suppressesIncrementalRendering = options.suppressesIncrementalRendering
configuration.selectionGranularity = WKSelectionGranularity.init(rawValue: options.selectionGranularity)! configuration.selectionGranularity = WKSelectionGranularity.init(rawValue: options.selectionGranularity)!
if options.allowUniversalAccessFromFileURLs {
configuration.setValue(options.allowUniversalAccessFromFileURLs, forKey: "allowUniversalAccessFromFileURLs")
}
if options.allowFileAccessFromFileURLs {
configuration.preferences.setValue(options.allowFileAccessFromFileURLs, forKey: "allowFileAccessFromFileURLs")
}
if #available(iOS 9.0, *) { if #available(iOS 9.0, *) {
if options.incognito { if options.incognito {
configuration.websiteDataStore = WKWebsiteDataStore.nonPersistent() configuration.websiteDataStore = WKWebsiteDataStore.nonPersistent()
@ -1748,6 +1756,14 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
} }
} }
if newOptionsMap["allowUniversalAccessFromFileURLs"] != nil && options?.allowUniversalAccessFromFileURLs != newOptions.allowUniversalAccessFromFileURLs {
configuration.setValue(newOptions.allowUniversalAccessFromFileURLs, forKey: "allowUniversalAccessFromFileURLs")
}
if newOptionsMap["allowFileAccessFromFileURLs"] != nil && options?.allowFileAccessFromFileURLs != newOptions.allowFileAccessFromFileURLs {
configuration.preferences.setValue(newOptions.allowFileAccessFromFileURLs, forKey: "allowFileAccessFromFileURLs")
}
if newOptionsMap["clearCache"] != nil && newOptions.clearCache { if newOptionsMap["clearCache"] != nil && newOptions.clearCache {
clearCache() clearCache()
} }
@ -2025,7 +2041,9 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic || if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic ||
challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodDefault || challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodDefault ||
challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPDigest { challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPDigest ||
challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodNegotiate ||
challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodNTLM {
let host = challenge.protectionSpace.host let host = challenge.protectionSpace.host
let prot = challenge.protectionSpace.protocol let prot = challenge.protectionSpace.protocol
let realm = challenge.protectionSpace.realm let realm = challenge.protectionSpace.realm
@ -2498,6 +2516,9 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
} }
if !handledByClient, InAppWebView.windowWebViews[windowId] != nil { if !handledByClient, InAppWebView.windowWebViews[windowId] != nil {
InAppWebView.windowWebViews.removeValue(forKey: windowId) InAppWebView.windowWebViews.removeValue(forKey: windowId)
if let url = navigationAction.request.url {
self.loadUrl(url: url, headers: nil)
}
} }
} }
}) })

View File

@ -18,7 +18,6 @@ public class InAppWebViewOptions: Options<InAppWebView> {
var userAgent = "" var userAgent = ""
var applicationNameForUserAgent = "" var applicationNameForUserAgent = ""
var javaScriptEnabled = true var javaScriptEnabled = true
var debuggingEnabled = true
var javaScriptCanOpenWindowsAutomatically = false var javaScriptCanOpenWindowsAutomatically = false
var mediaPlaybackRequiresUserGesture = true var mediaPlaybackRequiresUserGesture = true
var verticalScrollBarEnabled = true var verticalScrollBarEnabled = true
@ -35,6 +34,8 @@ public class InAppWebViewOptions: Options<InAppWebView> {
var disableHorizontalScroll = false var disableHorizontalScroll = false
var disableContextMenu = false var disableContextMenu = false
var supportZoom = true var supportZoom = true
var allowUniversalAccessFromFileURLs = false
var allowFileAccessFromFileURLs = false
var disallowOverScroll = false var disallowOverScroll = false
var enableViewportScale = false var enableViewportScale = false
@ -106,6 +107,8 @@ public class InAppWebViewOptions: Options<InAppWebView> {
realOptions["isPagingEnabled"] = webView.scrollView.isPagingEnabled realOptions["isPagingEnabled"] = webView.scrollView.isPagingEnabled
realOptions["maximumZoomScale"] = webView.scrollView.maximumZoomScale realOptions["maximumZoomScale"] = webView.scrollView.maximumZoomScale
realOptions["minimumZoomScale"] = webView.scrollView.minimumZoomScale realOptions["minimumZoomScale"] = webView.scrollView.minimumZoomScale
realOptions["allowUniversalAccessFromFileURLs"] = configuration.value(forKey: "allowUniversalAccessFromFileURLs")
realOptions["allowFileAccessFromFileURLs"] = configuration.preferences.value(forKey: "allowFileAccessFromFileURLs")
} }
return realOptions return realOptions
} }

View File

@ -146,7 +146,7 @@ class MyCookieManager: NSObject, FlutterPlugin {
if let urlHost = URL(string: url)?.host { if let urlHost = URL(string: url)?.host {
MyCookieManager.httpCookieStore!.getAllCookies { (cookies) in MyCookieManager.httpCookieStore!.getAllCookies { (cookies) in
for cookie in cookies { for cookie in cookies {
if cookie.domain.contains(urlHost) { if urlHost.hasSuffix(cookie.domain) {
var sameSite: String? = nil var sameSite: String? = nil
if #available(iOS 13.0, *) { if #available(iOS 13.0, *) {
if let sameSiteValue = cookie.sameSitePolicy?.rawValue { if let sameSiteValue = cookie.sameSitePolicy?.rawValue {

View File

@ -1,17 +1,16 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'asn1_identifier.dart'; import 'asn1_identifier.dart';
import 'asn1_object.dart'; import 'asn1_object.dart';
class ASN1DERDecoder { class ASN1DERDecoder {
static List<ASN1Object> decode({@required List<int> data}) { static List<ASN1Object> decode({required List<int> data}) {
var iterator = data.iterator; var iterator = data.iterator;
return parse(iterator: iterator); return parse(iterator: iterator);
} }
static List<ASN1Object> parse({@required Iterator<int> iterator}) { static List<ASN1Object> parse({required Iterator<int> iterator}) {
var result = <ASN1Object>[]; var result = <ASN1Object>[];
while (iterator.moveNext()) { while (iterator.moveNext()) {
@ -20,7 +19,7 @@ class ASN1DERDecoder {
var asn1obj = ASN1Object(); var asn1obj = ASN1Object();
asn1obj.identifier = ASN1Identifier(nextValue); asn1obj.identifier = ASN1Identifier(nextValue);
if (asn1obj.identifier.isConstructed()) { if (asn1obj.identifier!.isConstructed()) {
var contentData = loadSubContent(iterator: iterator); var contentData = loadSubContent(iterator: iterator);
if (contentData.isEmpty) { if (contentData.isEmpty) {
@ -34,18 +33,20 @@ class ASN1DERDecoder {
asn1obj.encoded = Uint8List.fromList(contentData); asn1obj.encoded = Uint8List.fromList(contentData);
for (var item in asn1obj.sub) { if (asn1obj.sub != null) {
item.parent = asn1obj; for (var item in asn1obj.sub!) {
item.parent = asn1obj;
}
} }
} else { } else {
if (asn1obj.identifier.typeClass() == ASN1IdentifierClass.UNIVERSAL) { if (asn1obj.identifier!.typeClass() == ASN1IdentifierClass.UNIVERSAL) {
var contentData = loadSubContent(iterator: iterator); var contentData = loadSubContent(iterator: iterator);
asn1obj.encoded = Uint8List.fromList(contentData); asn1obj.encoded = Uint8List.fromList(contentData);
// decode the content data with come more convenient format // decode the content data with come more convenient format
var tagNumber = asn1obj.identifier.tagNumber(); var tagNumber = asn1obj.identifier!.tagNumber();
if (tagNumber == ASN1IdentifierTagNumber.END_OF_CONTENT) { if (tagNumber == ASN1IdentifierTagNumber.END_OF_CONTENT) {
return result; return result;
@ -130,9 +131,12 @@ class ASN1DERDecoder {
return result; return result;
} }
static BigInt getContentLength({@required Iterator<int> iterator}) { static BigInt getContentLength({required Iterator<int> iterator}) {
if (iterator.moveNext()) { if (iterator.moveNext()) {
var first = iterator.current; int? first;
try {
first = iterator.current;
} catch (e) {}
if (first != null) { if (first != null) {
if ((first & 0x80) != 0) { if ((first & 0x80) != 0) {
// long // long
@ -141,7 +145,10 @@ class ASN1DERDecoder {
var data = <int>[]; var data = <int>[];
for (var i = 0; i < octetsToRead; i++) { for (var i = 0; i < octetsToRead; i++) {
if (iterator.moveNext()) { if (iterator.moveNext()) {
var n = iterator.current; int? n;
try {
n = iterator.current;
} catch (e) {}
if (n != null) { if (n != null) {
data.add(n); data.add(n);
} }
@ -158,7 +165,7 @@ class ASN1DERDecoder {
return BigInt.from(0); return BigInt.from(0);
} }
static List<int> loadSubContent({@required Iterator<int> iterator}) { static List<int> loadSubContent({required Iterator<int> iterator}) {
var len = getContentLength(iterator: iterator); var len = getContentLength(iterator: iterator);
int int64MaxValue = double.maxFinite.toInt(); int int64MaxValue = double.maxFinite.toInt();
@ -170,7 +177,10 @@ class ASN1DERDecoder {
for (var i = 0; i < len.toInt(); i++) { for (var i = 0; i < len.toInt(); i++) {
if (iterator.moveNext()) { if (iterator.moveNext()) {
var n = iterator.current; int? n;
try {
n = iterator.current;
} catch (e) {}
if (n != null) { if (n != null) {
byteArray.add(n); byteArray.add(n);
} }
@ -183,7 +193,7 @@ class ASN1DERDecoder {
} }
/// Decode DER OID bytes to String with dot notation /// Decode DER OID bytes to String with dot notation
static String decodeOid({@required List<int> contentData}) { static String decodeOid({required List<int> contentData}) {
if (contentData.isEmpty) { if (contentData.isEmpty) {
return ""; return "";
} }
@ -211,7 +221,7 @@ class ASN1DERDecoder {
///dates past 2049. Parsing that structure hasn't been implemented yet. ///dates past 2049. Parsing that structure hasn't been implemented yet.
/// ///
///[contentData] the UTCTime value to convert. ///[contentData] the UTCTime value to convert.
static DateTime utcTimeToDate({@required List<int> contentData}) { static DateTime? utcTimeToDate({required List<int> contentData}) {
/* The following formats can be used: /* The following formats can be used:
YYMMDDhhmmZ YYMMDDhhmmZ
YYMMDDhhmm+hh'mm' YYMMDDhhmm+hh'mm'
@ -231,7 +241,7 @@ class ASN1DERDecoder {
hh' is the absolute value of the offset from GMT in hours hh' is the absolute value of the offset from GMT in hours
mm' is the absolute value of the offset from GMT in minutes */ mm' is the absolute value of the offset from GMT in minutes */
String utc; String? utc;
try { try {
utc = utf8.decode(contentData); utc = utf8.decode(contentData);
} catch (e) {} } catch (e) {}
@ -250,8 +260,8 @@ class ASN1DERDecoder {
var mm = int.parse(utc.substring(8, 10), radix: 10); var mm = int.parse(utc.substring(8, 10), radix: 10);
var ss = 0; var ss = 0;
int end; int? end;
String c; String? c;
// not just YYMMDDhhmmZ // not just YYMMDDhhmmZ
if (utc.length > 11) { if (utc.length > 11) {
// get character after minutes // get character after minutes
@ -298,7 +308,7 @@ class ASN1DERDecoder {
///Converts a GeneralizedTime value to a date. ///Converts a GeneralizedTime value to a date.
/// ///
///[contentData] the GeneralizedTime value to convert. ///[contentData] the GeneralizedTime value to convert.
static DateTime generalizedTimeToDate({@required List<int> contentData}) { static DateTime? generalizedTimeToDate({required List<int> contentData}) {
/* The following formats can be used: /* The following formats can be used:
YYYYMMDDHHMMSS YYYYMMDDHHMMSS
YYYYMMDDHHMMSS.fff YYYYMMDDHHMMSS.fff
@ -321,7 +331,7 @@ class ASN1DERDecoder {
hh' is the absolute value of the offset from GMT in hours hh' is the absolute value of the offset from GMT in hours
mm' is the absolute value of the offset from GMT in minutes */ mm' is the absolute value of the offset from GMT in minutes */
String gentime; String? gentime;
try { try {
gentime = utf8.decode(contentData); gentime = utf8.decode(contentData);
} catch (e) {} } catch (e) {}
@ -385,7 +395,7 @@ class ASN1DERDecoder {
} }
} }
BigInt toIntValue(List<int> data) { BigInt? toIntValue(List<int> data) {
if (data.length > 8) { if (data.length > 8) {
return null; return null;
} }

View File

@ -19,10 +19,15 @@ class ASN1DistinguishedNames {
ASN1DistinguishedNames.EMAIL, ASN1DistinguishedNames.EMAIL,
].toSet(); ].toSet();
static ASN1DistinguishedNames fromValue(String oid) { static ASN1DistinguishedNames? fromValue(String? oid) {
if (oid != null) if (oid != null) {
return ASN1DistinguishedNames.values try {
.firstWhere((element) => element.oid() == oid, orElse: () => null); return ASN1DistinguishedNames.values
.firstWhere((element) => element.oid() == oid);
} catch (e) {
return null;
}
}
return null; return null;
} }

View File

@ -10,11 +10,15 @@ class ASN1IdentifierClass {
ASN1IdentifierClass.PRIVATE, ASN1IdentifierClass.PRIVATE,
].toSet(); ].toSet();
static ASN1IdentifierClass fromValue(int value) { static ASN1IdentifierClass? fromValue(int? value) {
if (value != null) if (value != null) {
return ASN1IdentifierClass.values.firstWhere( try {
(element) => element.toValue() == value, return ASN1IdentifierClass.values.firstWhere(
orElse: () => null); (element) => element.toValue() == value);
} catch (e) {
return null;
}
}
return null; return null;
} }
@ -83,11 +87,15 @@ class ASN1IdentifierTagNumber {
ASN1IdentifierTagNumber.BMP_STRING, ASN1IdentifierTagNumber.BMP_STRING,
].toSet(); ].toSet();
static ASN1IdentifierTagNumber fromValue(int value) { static ASN1IdentifierTagNumber? fromValue(int? value) {
if (value != null) if (value != null) {
return ASN1IdentifierTagNumber.values.firstWhere( try {
(element) => element.toValue() == value, return ASN1IdentifierTagNumber.values.firstWhere(
orElse: () => null); (element) => element.toValue() == value);
} catch (e) {
return null;
}
}
return null; return null;
} }

View File

@ -7,34 +7,34 @@ import 'oid.dart';
class ASN1Object { class ASN1Object {
/// This property contains the DER encoded object /// This property contains the DER encoded object
Uint8List encoded; Uint8List? encoded;
/// This property contains the decoded Swift object whenever is possible /// This property contains the decoded Swift object whenever is possible
dynamic value; dynamic value;
ASN1Identifier identifier; ASN1Identifier? identifier;
List<ASN1Object> sub; List<ASN1Object>? sub;
ASN1Object parent; ASN1Object? parent;
ASN1Object subAtIndex(int index) { ASN1Object? subAtIndex(int index) {
if (sub != null && index >= 0 && index < sub.length) { if (sub != null && index >= 0 && index < sub!.length) {
return sub[index]; return sub![index];
} }
return null; return null;
} }
ASN1Object firstSub() { ASN1Object? firstSub() {
if (subCount() > 0) { if (subCount() > 0) {
return sub.first; return sub!.first;
} }
return null; return null;
} }
ASN1Object lastSub() { ASN1Object? lastSub() {
if (subCount() > 0) { if (subCount() > 0) {
return sub.last; return sub!.last;
} }
return null; return null;
} }
@ -43,7 +43,7 @@ class ASN1Object {
return sub?.length ?? 0; return sub?.length ?? 0;
} }
ASN1Object findOid({OID oid, String oidValue}) { ASN1Object? findOid({OID? oid, String? oidValue}) {
oidValue = oid != null ? oid.toValue() : oidValue; oidValue = oid != null ? oid.toValue() : oidValue;
for (var child in (sub ?? <ASN1Object>[])) { for (var child in (sub ?? <ASN1Object>[])) {
if (child.identifier?.tagNumber() == if (child.identifier?.tagNumber() ==
@ -67,7 +67,7 @@ class ASN1Object {
String printAsn1({insets = ""}) { String printAsn1({insets = ""}) {
var output = insets; var output = insets;
output += identifier?.description?.toUpperCase() ?? ""; output += identifier?.description.toUpperCase() ?? "";
output += (value != null ? ": $value" : ""); output += (value != null ? ": $value" : "");
if (identifier?.typeClass() == ASN1IdentifierClass.UNIVERSAL && if (identifier?.typeClass() == ASN1IdentifierClass.UNIVERSAL &&
identifier?.tagNumber() == ASN1IdentifierTagNumber.OBJECT_IDENTIFIER) { identifier?.tagNumber() == ASN1IdentifierTagNumber.OBJECT_IDENTIFIER) {
@ -76,12 +76,12 @@ class ASN1Object {
output += " ($descr)"; output += " ($descr)";
} }
} }
output += sub != null && sub.length > 0 ? " {" : ""; output += sub != null && sub!.length > 0 ? " {" : "";
output += "\n"; output += "\n";
for (var item in (sub ?? <ASN1Object>[])) { for (var item in (sub ?? <ASN1Object>[])) {
output += item.printAsn1(insets: insets + " "); output += item.printAsn1(insets: insets + " ");
} }
output += sub != null && sub.length > 0 ? "}\n" : ""; output += sub != null && sub!.length > 0 ? "}\n" : "";
return output; return output;
} }
@ -90,9 +90,9 @@ class ASN1Object {
return description; return description;
} }
ASN1Object atIndex(X509BlockPosition x509blockPosition) { ASN1Object? atIndex(X509BlockPosition x509blockPosition) {
if (sub != null && x509blockPosition.index < sub.length) { if (sub != null && x509blockPosition.index < sub!.length) {
return sub[x509blockPosition.index]; return sub![x509blockPosition.index];
} }
return null; return null;
} }

View File

@ -15,14 +15,20 @@ class KeyUsage {
KeyUsage.decipherOnly, KeyUsage.decipherOnly,
].toSet(); ].toSet();
static KeyUsage fromIndex(int value) { static KeyUsage? fromIndex(int? value) {
return KeyUsage.values.firstWhere((element) => element.toValue() == value, if (value != null) {
orElse: () => null); try {
return KeyUsage.values.firstWhere((element) => element.toValue() == value);
} catch (e) {
return null;
}
}
return null;
} }
int toValue() => _value; int toValue() => _value;
String name() => _KeyUsageMapName[this._value]; String name() => _KeyUsageMapName.containsKey(this._value) ? _KeyUsageMapName[this._value]! : "";
@override @override
String toString() => "($_value, ${name()})"; String toString() => "($_value, ${name()})";

View File

@ -102,14 +102,20 @@ class OID {
OID.timeStamping, OID.timeStamping,
].toSet(); ].toSet();
static OID fromValue(String value) { static OID? fromValue(String? value) {
return OID.values.firstWhere((element) => element.toValue() == value, if (value != null) {
orElse: () => null); try {
return OID.values.firstWhere((element) => element.toValue() == value);
} catch (e) {
return null;
}
}
return null;
} }
String toValue() => _value; String toValue() => _value;
String name() => _oidMapName[this._value]; String name() => _oidMapName.containsKey(this._value) ? _oidMapName[this._value]! : "";
@override @override
String toString() => "($_value, ${name()})"; String toString() => "($_value, ${name()})";

View File

@ -1,7 +1,6 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'asn1_decoder.dart'; import 'asn1_decoder.dart';
import 'asn1_object.dart'; import 'asn1_object.dart';
import 'oid.dart'; import 'oid.dart';
@ -13,19 +12,19 @@ import 'asn1_distinguished_names.dart';
///Class that represents a X.509 certificate. ///Class that represents a X.509 certificate.
///This provides a standard way to access all the attributes of an X.509 certificate. ///This provides a standard way to access all the attributes of an X.509 certificate.
class X509Certificate { class X509Certificate {
List<ASN1Object> asn1; List<ASN1Object>? asn1;
ASN1Object block1; ASN1Object? block1;
///Returns the encoded form of this certificate. It is ///Returns the encoded form of this certificate. It is
///assumed that each certificate type would have only a single ///assumed that each certificate type would have only a single
///form of encoding; for example, X.509 certificates would ///form of encoding; for example, X.509 certificates would
///be encoded as ASN.1 DER. ///be encoded as ASN.1 DER.
Uint8List encoded; Uint8List? encoded;
static const beginPemBlock = "-----BEGIN CERTIFICATE-----"; static const beginPemBlock = "-----BEGIN CERTIFICATE-----";
static const endPemBlock = "-----END CERTIFICATE-----"; static const endPemBlock = "-----END CERTIFICATE-----";
X509Certificate({ASN1Object asn1}) { X509Certificate({ASN1Object? asn1}) {
if (asn1 != null) { if (asn1 != null) {
var block1 = asn1.subAtIndex(0); var block1 = asn1.subAtIndex(0);
if (block1 == null) { if (block1 == null) {
@ -34,7 +33,7 @@ class X509Certificate {
} }
} }
static X509Certificate fromData({@required Uint8List data}) { static X509Certificate fromData({required Uint8List data}) {
var decoded = utf8.decode(data, allowMalformed: true); var decoded = utf8.decode(data, allowMalformed: true);
if (decoded.contains(X509Certificate.beginPemBlock)) { if (decoded.contains(X509Certificate.beginPemBlock)) {
return X509Certificate.fromPemData(pem: data); return X509Certificate.fromPemData(pem: data);
@ -43,10 +42,10 @@ class X509Certificate {
} }
} }
static X509Certificate fromDerData({@required Uint8List der}) { static X509Certificate fromDerData({required Uint8List der}) {
var asn1 = ASN1DERDecoder.decode(data: der.toList(growable: true)); var asn1 = ASN1DERDecoder.decode(data: der.toList(growable: true));
if (asn1.length > 0) { if (asn1.length > 0) {
var block1 = asn1.first?.subAtIndex(0); var block1 = asn1.first.subAtIndex(0);
if (block1 != null) { if (block1 != null) {
var certificate = X509Certificate(); var certificate = X509Certificate();
certificate.asn1 = asn1; certificate.asn1 = asn1;
@ -58,7 +57,7 @@ class X509Certificate {
throw ASN1ParseError(); throw ASN1ParseError();
} }
static X509Certificate fromPemData({@required Uint8List pem}) { static X509Certificate fromPemData({required Uint8List pem}) {
var derData = X509Certificate.decodeToDER(pemData: pem); var derData = X509Certificate.decodeToDER(pemData: pem);
if (derData == null) { if (derData == null) {
throw ASN1ParseError(); throw ASN1ParseError();
@ -67,9 +66,9 @@ class X509Certificate {
} }
///Read possible PEM encoding ///Read possible PEM encoding
static Uint8List decodeToDER({@required pemData}) { static Uint8List? decodeToDER({required pemData}) {
var pem = String.fromCharCodes(pemData); var pem = String.fromCharCodes(pemData);
if (pem != null && pem.contains(X509Certificate.beginPemBlock)) { if (pem.contains(X509Certificate.beginPemBlock)) {
var lines = pem.split("\n"); var lines = pem.split("\n");
var base64buffer = ""; var base64buffer = "";
var certLine = false; var certLine = false;
@ -85,7 +84,7 @@ class X509Certificate {
} }
} }
Uint8List derDataDecoded; Uint8List? derDataDecoded;
try { try {
derDataDecoded = Uint8List.fromList(utf8.encode(base64buffer)); derDataDecoded = Uint8List.fromList(utf8.encode(base64buffer));
} catch (e) {} } catch (e) {}
@ -97,26 +96,28 @@ class X509Certificate {
} }
String get description => String get description =>
asn1.fold("", (value, element) => value + element.description + "\n"); asn1?.fold("", (value, element) => (value ?? '') + element.description + '\n') ?? '';
///Checks that the given date is within the certificate's validity period. ///Checks that the given date is within the certificate's validity period.
bool checkValidity({DateTime date}) { bool checkValidity({DateTime? date}) {
if (date == null) { if (date == null) {
date = DateTime.now(); date = DateTime.now();
} }
if (notBefore != null && notAfter != null) { if (notBefore != null && notAfter != null) {
return date.isAfter(notBefore) && date.isBefore(notAfter); return date.isAfter(notBefore!) && date.isBefore(notAfter!);
} }
return false; return false;
} }
///Gets the version (version number) value from the certificate. ///Gets the version (version number) value from the certificate.
int get version { int? get version {
var v = firstLeafValue(block: block1) as List<int>; if (block1 != null) {
if (v != null) { var v = firstLeafValue(block: block1!) as List<int>?;
var index = toIntValue(v); if (v != null) {
if (index != null) { var index = toIntValue(v);
return index.toInt() + 1; if (index != null) {
return index.toInt() + 1;
}
} }
} }
return null; return null;
@ -124,11 +125,11 @@ class X509Certificate {
///Gets the serialNumber value from the certificate. ///Gets the serialNumber value from the certificate.
List<int> get serialNumber => List<int> get serialNumber =>
block1.atIndex(X509BlockPosition.serialNumber)?.value as List<int>; block1?.atIndex(X509BlockPosition.serialNumber)?.value as List<int>;
///Returns the issuer (issuer distinguished name) value from the certificate as a String. ///Returns the issuer (issuer distinguished name) value from the certificate as a String.
String get issuerDistinguishedName { String? get issuerDistinguishedName {
var issuerBlock = block1.atIndex(X509BlockPosition.issuer); var issuerBlock = block1?.atIndex(X509BlockPosition.issuer);
if (issuerBlock != null) { if (issuerBlock != null) {
return blockDistinguishedName(block: issuerBlock); return blockDistinguishedName(block: issuerBlock);
} }
@ -137,10 +138,10 @@ class X509Certificate {
List<String> get issuerOIDs { List<String> get issuerOIDs {
var result = <String>[]; var result = <String>[];
var issuerBlock = block1.atIndex(X509BlockPosition.issuer); var issuerBlock = block1?.atIndex(X509BlockPosition.issuer);
if (issuerBlock != null) { if (issuerBlock != null) {
for (var sub in (issuerBlock.sub ?? <ASN1Object>[])) { for (var sub in (issuerBlock.sub ?? <ASN1Object>[])) {
var value = firstLeafValue(block: sub) as String; var value = firstLeafValue(block: sub) as String?;
if (value != null) { if (value != null) {
result.add(value); result.add(value);
} }
@ -149,12 +150,12 @@ class X509Certificate {
return result; return result;
} }
String issuer({String oid, ASN1DistinguishedNames dn}) { String? issuer({String? oid, ASN1DistinguishedNames? dn}) {
if (oid == null && dn != null) { if (oid == null && dn != null) {
oid = dn.oid(); oid = dn.oid();
} }
if (oid != null) { if (oid != null) {
var issuerBlock = block1.atIndex(X509BlockPosition.issuer); var issuerBlock = block1?.atIndex(X509BlockPosition.issuer);
if (issuerBlock != null) { if (issuerBlock != null) {
var oidBlock = issuerBlock.findOid(oidValue: oid); var oidBlock = issuerBlock.findOid(oidValue: oid);
if (oidBlock != null) { if (oidBlock != null) {
@ -171,8 +172,8 @@ class X509Certificate {
} }
///Returns the subject (subject distinguished name) value from the certificate as a String. ///Returns the subject (subject distinguished name) value from the certificate as a String.
String get subjectDistinguishedName { String? get subjectDistinguishedName {
var subjectBlock = block1.atIndex(X509BlockPosition.subject); var subjectBlock = block1?.atIndex(X509BlockPosition.subject);
if (subjectBlock != null) { if (subjectBlock != null) {
return blockDistinguishedName(block: subjectBlock); return blockDistinguishedName(block: subjectBlock);
} }
@ -181,10 +182,10 @@ class X509Certificate {
List<String> get subjectOIDs { List<String> get subjectOIDs {
var result = <String>[]; var result = <String>[];
var subjectBlock = block1.atIndex(X509BlockPosition.subject); var subjectBlock = block1?.atIndex(X509BlockPosition.subject);
if (subjectBlock != null) { if (subjectBlock != null) {
for (var sub in (subjectBlock.sub ?? <ASN1Object>[])) { for (var sub in (subjectBlock.sub ?? <ASN1Object>[])) {
var value = firstLeafValue(block: sub) as String; var value = firstLeafValue(block: sub) as String?;
if (value != null) { if (value != null) {
result.add(value); result.add(value);
} }
@ -193,12 +194,12 @@ class X509Certificate {
return result; return result;
} }
String subject({String oid, ASN1DistinguishedNames dn}) { String? subject({String? oid, ASN1DistinguishedNames? dn}) {
if (oid == null && dn != null) { if (oid == null && dn != null) {
oid = dn.oid(); oid = dn.oid();
} }
if (oid != null) { if (oid != null) {
var subjectBlock = block1.atIndex(X509BlockPosition.subject); var subjectBlock = block1?.atIndex(X509BlockPosition.subject);
if (subjectBlock != null) { if (subjectBlock != null) {
var oidBlock = subjectBlock.findOid(oidValue: oid); var oidBlock = subjectBlock.findOid(oidValue: oid);
if (oidBlock != null) { if (oidBlock != null) {
@ -215,30 +216,29 @@ class X509Certificate {
} }
///Gets the notBefore date from the validity period of the certificate. ///Gets the notBefore date from the validity period of the certificate.
DateTime get notBefore => DateTime? get notBefore =>
block1.atIndex(X509BlockPosition.dateValidity)?.subAtIndex(0)?.value block1?.atIndex(X509BlockPosition.dateValidity)?.subAtIndex(0)?.value
as DateTime; as DateTime?;
///Gets the notAfter date from the validity period of the certificate. ///Gets the notAfter date from the validity period of the certificate.
DateTime get notAfter { DateTime? get notAfter {
var value = block1 var value = block1?.atIndex(X509BlockPosition.dateValidity)
.atIndex(X509BlockPosition.dateValidity)
?.subAtIndex(1) ?.subAtIndex(1)
?.value as DateTime; ?.value as DateTime?;
return value; return value;
} }
///Gets the signature value (the raw signature bits) from the certificate. ///Gets the signature value (the raw signature bits) from the certificate.
List<int> get signature => asn1[0].subAtIndex(2)?.value as List<int>; List<int>? get signature => asn1?[0].subAtIndex(2)?.value as List<int>;
///Gets the signature algorithm name for the certificate signature algorithm. ///Gets the signature algorithm name for the certificate signature algorithm.
String get sigAlgName => OID.fromValue(sigAlgOID ?? "")?.name(); String? get sigAlgName => OID.fromValue(sigAlgOID ?? '')?.name();
///Gets the signature algorithm OID string from the certificate. ///Gets the signature algorithm OID string from the certificate.
String get sigAlgOID => block1.subAtIndex(2)?.subAtIndex(0)?.value as String; String? get sigAlgOID => block1?.subAtIndex(2)?.subAtIndex(0)?.value as String?;
///Gets the DER-encoded signature algorithm parameters from this certificate's signature algorithm. ///Gets the DER-encoded signature algorithm parameters from this certificate's signature algorithm.
List<int> get sigAlgParams => null; List<int>? get sigAlgParams => null;
///Gets a boolean array representing bits of the KeyUsage extension, (OID = 2.5.29.15). ///Gets a boolean array representing bits of the KeyUsage extension, (OID = 2.5.29.15).
///``` ///```
@ -256,12 +256,12 @@ class X509Certificate {
///``` ///```
List<bool> get keyUsage { List<bool> get keyUsage {
var result = <bool>[]; var result = <bool>[];
var oidBlock = block1.findOid(oid: OID.keyUsage); var oidBlock = block1?.findOid(oid: OID.keyUsage);
if (oidBlock != null) { if (oidBlock != null) {
var sub = oidBlock.parent?.sub; var sub = oidBlock.parent?.sub;
if (sub != null && sub.length > 0) { if (sub != null && sub.length > 0) {
var data = sub.last.subAtIndex(0)?.value as List<int>; var data = sub.last.subAtIndex(0)?.value as List<int>?;
int bits = (data != null && data.length > 0) ? data.first ?? 0 : 0; int bits = (data != null && data.length > 0) ? data.first : 0;
for (var index = 0; index < 8; index++) { for (var index = 0; index < 8; index++) {
var value = bits & (1 << index).toUnsigned(8) != 0; var value = bits & (1 << index).toUnsigned(8) != 0;
result.insert(0, value); result.insert(0, value);
@ -285,8 +285,8 @@ class X509Certificate {
extensionObject(oid: OID.issuerAltName)?.valueAsStrings ?? <String>[]; extensionObject(oid: OID.issuerAltName)?.valueAsStrings ?? <String>[];
///Gets the informations of the public key from this certificate. ///Gets the informations of the public key from this certificate.
X509PublicKey get publicKey { X509PublicKey? get publicKey {
var pkBlock = block1.atIndex(X509BlockPosition.publicKey); var pkBlock = block1?.atIndex(X509BlockPosition.publicKey);
if (pkBlock != null) { if (pkBlock != null) {
return X509PublicKey(pkBlock: pkBlock); return X509PublicKey(pkBlock: pkBlock);
} }
@ -302,7 +302,7 @@ class X509Certificate {
return extensionBlocks return extensionBlocks
.map((block) => X509Extension(block: block)) .map((block) => X509Extension(block: block))
.where((extension) => extension.isCritical) .where((extension) => extension.isCritical)
.map((extension) => extension.oid) .map((extension) => extension.oid ?? '')
.toList(); .toList();
} }
@ -315,7 +315,7 @@ class X509Certificate {
return extensionBlocks return extensionBlocks
.map((block) => X509Extension(block: block)) .map((block) => X509Extension(block: block))
.where((extension) => !extension.isCritical) .where((extension) => !extension.isCritical)
.map((extension) => extension.oid) .map((extension) => extension.oid ?? '')
.toList(); .toList();
} }
@ -362,7 +362,7 @@ class X509Certificate {
?.firstSub() ?.firstSub()
?.sub ?.sub
?.map((e) => e.firstSub()?.value as String) ?.map((e) => e.firstSub()?.value as String)
?.toList() ?? .toList() ??
<String>[]; <String>[];
///Gets the list of CRL distribution points from the CRLDistributionPoints extension, (OID = 2.5.29.31). ///Gets the list of CRL distribution points from the CRLDistributionPoints extension, (OID = 2.5.29.31).
@ -373,7 +373,7 @@ class X509Certificate {
?.firstSub() ?.firstSub()
?.sub ?.sub
?.map((e) => e.firstSub()?.firstSub()?.firstSub()?.value as String) ?.map((e) => e.firstSub()?.firstSub()?.firstSub()?.value as String)
?.toList() ?? .toList() ??
<String>[]; <String>[];
///Gets the map of the format (as a key) and location (as a value) of additional information ///Gets the map of the format (as a key) and location (as a value) of additional information
@ -390,24 +390,23 @@ class X509Certificate {
sub.forEach((element) { sub.forEach((element) {
if (element.subCount() > 1) { if (element.subCount() > 1) {
result.putIfAbsent( result.putIfAbsent(
element.subAtIndex(0).value, () => element.subAtIndex(1).value); element.subAtIndex(0)!.value, () => element.subAtIndex(1)!.value);
} }
}); });
} }
return result; return result;
} }
List<ASN1Object> get extensionBlocks => List<ASN1Object>? get extensionBlocks =>
block1.atIndex(X509BlockPosition.extensions)?.subAtIndex(0)?.sub; block1?.atIndex(X509BlockPosition.extensions)?.subAtIndex(0)?.sub;
///Gets the extension information of the given OID code or enum string value. ///Gets the extension information of the given OID code or enum string value.
X509Extension extensionObject({String oidValue, OID oid}) { X509Extension? extensionObject({String? oidValue, OID? oid}) {
if (oidValue == null && oid != null) { if (oidValue == null && oid != null) {
oidValue = oid.toValue(); oidValue = oid.toValue();
} }
if (oidValue != null) { if (oidValue != null) {
var block = block1 var block = block1?.atIndex(X509BlockPosition.extensions)
.atIndex(X509BlockPosition.extensions)
?.findOid(oidValue: oidValue) ?.findOid(oidValue: oidValue)
?.parent; ?.parent;
if (block != null) { if (block != null) {
@ -418,7 +417,7 @@ class X509Certificate {
} }
///Format subject/issuer information in RFC1779 ///Format subject/issuer information in RFC1779
String blockDistinguishedName({@required ASN1Object block}) { String blockDistinguishedName({required ASN1Object block}) {
var result = ""; var result = "";
for (var oidName in ASN1DistinguishedNames.values) { for (var oidName in ASN1DistinguishedNames.values) {
var oidBlock = block.findOid(oidValue: oidName.oid()); var oidBlock = block.findOid(oidValue: oidName.oid());
@ -431,7 +430,7 @@ class X509Certificate {
var sub = oidBlock.parent?.sub; var sub = oidBlock.parent?.sub;
if (sub != null && sub.length > 0) { if (sub != null && sub.length > 0) {
var value = sub.last.value as String; var value = sub.last.value as String?;
if (value != null) { if (value != null) {
var specialChar = ",+=\n<>#;\\"; var specialChar = ",+=\n<>#;\\";
var quote = ""; var quote = "";
@ -490,10 +489,13 @@ class X509Certificate {
} }
} }
dynamic firstLeafValue({@required ASN1Object block}) { dynamic firstLeafValue({required ASN1Object block}) {
var sub = block.sub; var sub = block.sub;
if (sub != null && sub.length > 0) { if (sub != null && sub.length > 0) {
var subFirst = sub.first; ASN1Object? subFirst;
try {
subFirst = sub.first;
} catch (e) {}
if (subFirst != null) { if (subFirst != null) {
return firstLeafValue(block: subFirst); return firstLeafValue(block: subFirst);
} }

View File

@ -3,25 +3,28 @@ import 'asn1_object.dart';
import 'oid.dart'; import 'oid.dart';
class X509Extension { class X509Extension {
ASN1Object block; ASN1Object? block;
X509Extension({this.block}); X509Extension({required this.block});
String get oid => block.subAtIndex(0)?.value; String? get oid => block?.subAtIndex(0)?.value;
String get name => OID.fromValue(oid ?? "")?.name(); String? get name => OID.fromValue(oid)?.name();
bool get isCritical { bool get isCritical {
if ((block.sub?.length ?? 0) > 2) { if ((block?.sub?.length ?? 0) > 2) {
return block.subAtIndex(1)?.value ?? false; return block?.subAtIndex(1)?.value ?? false;
} }
return false; return false;
} }
dynamic get value { dynamic get value {
var sub = block.sub; var sub = block?.sub;
if (sub != null && sub.length > 0) { if (sub != null && sub.length > 0) {
var valueBlock = sub.last; ASN1Object? valueBlock;
try {
valueBlock = sub.last;
} catch (e) {}
if (valueBlock != null) { if (valueBlock != null) {
return firstLeafValue(block: valueBlock); return firstLeafValue(block: valueBlock);
} }
@ -29,19 +32,23 @@ class X509Extension {
return null; return null;
} }
ASN1Object get valueAsBlock { ASN1Object? get valueAsBlock {
var sub = block.sub; var sub = block?.sub;
if (sub != null && sub.length > 0) { if (sub != null && sub.length > 0) {
return sub.last; ASN1Object? valueBlock;
try {
valueBlock = sub.last;
} catch (e) {}
return valueBlock;
} }
return null; return null;
} }
List<String> get valueAsStrings { List<String> get valueAsStrings {
var result = <String>[]; var result = <String>[];
var sub; var sub = <ASN1Object>[];
try { try {
sub = block.sub?.last?.sub?.last?.sub ?? <ASN1Object>[]; sub = block?.sub?.last.sub?.last.sub ?? <ASN1Object>[];
} catch (e) {} } catch (e) {}
for (var item in sub) { for (var item in sub) {

View File

@ -5,17 +5,17 @@ import 'asn1_object.dart';
import 'oid.dart'; import 'oid.dart';
class X509PublicKey { class X509PublicKey {
ASN1Object pkBlock; ASN1Object? pkBlock;
X509PublicKey({this.pkBlock}); X509PublicKey({this.pkBlock});
String get algOid => pkBlock?.subAtIndex(0)?.subAtIndex(0)?.value; String? get algOid => pkBlock?.subAtIndex(0)?.subAtIndex(0)?.value;
String get algName => OID.fromValue(algOid ?? "")?.name(); String? get algName => OID.fromValue(algOid)?.name();
String get algParams => pkBlock?.subAtIndex(0)?.subAtIndex(1)?.value; String? get algParams => pkBlock?.subAtIndex(0)?.subAtIndex(1)?.value;
Uint8List get encoded { Uint8List? get encoded {
var oid = OID.fromValue(algOid); var oid = OID.fromValue(algOid);
var keyData = pkBlock?.subAtIndex(1)?.value ?? null; var keyData = pkBlock?.subAtIndex(1)?.value ?? null;
@ -23,7 +23,7 @@ class X509PublicKey {
if (oid == OID.ecPublicKey) { if (oid == OID.ecPublicKey) {
return Uint8List.fromList(keyData); return Uint8List.fromList(keyData);
} else if (oid == OID.rsaEncryption) { } else if (oid == OID.rsaEncryption) {
List<ASN1Object> publicKeyAsn1Objects; List<ASN1Object>? publicKeyAsn1Objects;
try { try {
publicKeyAsn1Objects = publicKeyAsn1Objects =
ASN1DERDecoder.decode(data: keyData.toList(growable: true)); ASN1DERDecoder.decode(data: keyData.toList(growable: true));
@ -31,7 +31,7 @@ class X509PublicKey {
if (publicKeyAsn1Objects != null && publicKeyAsn1Objects.length > 0) { if (publicKeyAsn1Objects != null && publicKeyAsn1Objects.length > 0) {
var publicKeyModulus = var publicKeyModulus =
publicKeyAsn1Objects.first?.subAtIndex(0)?.value; publicKeyAsn1Objects.first.subAtIndex(0)?.value;
if (publicKeyModulus != null) { if (publicKeyModulus != null) {
return Uint8List.fromList(publicKeyModulus); return Uint8List.fromList(publicKeyModulus);
} }

View File

@ -1,7 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:collection'; import 'dart:collection';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'types.dart'; import 'types.dart';
@ -14,11 +13,11 @@ import 'in_app_browser.dart';
/// ///
///Note: Requires modification of Android 11+: https://developers.google.com/web/android/custom-tabs/best-practices#applications_targeting_android_11_api_level_30_or_above ///Note: Requires modification of Android 11+: https://developers.google.com/web/android/custom-tabs/best-practices#applications_targeting_android_11_api_level_30_or_above
class ChromeSafariBrowser { class ChromeSafariBrowser {
String uuid; late String uuid;
InAppBrowser browserFallback; InAppBrowser? browserFallback;
Map<int, ChromeSafariBrowserMenuItem> _menuItems = new HashMap(); Map<int, ChromeSafariBrowserMenuItem> _menuItems = new HashMap();
bool _isOpened = false; bool _isOpened = false;
MethodChannel _channel; late MethodChannel _channel;
static const MethodChannel _sharedChannel = static const MethodChannel _sharedChannel =
const MethodChannel('com.pichillilorenzo/flutter_chromesafaribrowser'); const MethodChannel('com.pichillilorenzo/flutter_chromesafaribrowser');
@ -48,7 +47,9 @@ class ChromeSafariBrowser {
String url = call.arguments["url"]; String url = call.arguments["url"];
String title = call.arguments["title"]; String title = call.arguments["title"];
int id = call.arguments["id"].toInt(); int id = call.arguments["id"].toInt();
this._menuItems[id].action(url, title); if (this._menuItems[id] != null) {
this._menuItems[id]!.action(url, title);
}
break; break;
default: default:
throw UnimplementedError("Unimplemented ${call.method} method"); throw UnimplementedError("Unimplemented ${call.method} method");
@ -67,14 +68,14 @@ class ChromeSafariBrowser {
/// ///
///[contextMenuFallback]: Context Menu used by the [InAppBrowser] instance fallback. ///[contextMenuFallback]: Context Menu used by the [InAppBrowser] instance fallback.
Future<void> open( Future<void> open(
{@required String url, {required String url,
ChromeSafariBrowserClassOptions options, ChromeSafariBrowserClassOptions? options,
Map<String, String> headersFallback = const {}, Map<String, String>? headersFallback = const {},
InAppBrowserClassOptions optionsFallback}) async { InAppBrowserClassOptions? optionsFallback}) async {
assert(url != null && url.isNotEmpty); assert(url.isNotEmpty);
this.throwIsAlreadyOpened(message: 'Cannot open $url!'); this.throwIsAlreadyOpened(message: 'Cannot open $url!');
List<Map<String, dynamic>> menuItemList = new List(); List<Map<String, dynamic>> menuItemList = [];
_menuItems.forEach((key, value) { _menuItems.forEach((key, value) {
menuItemList.add({"id": value.id, "label": value.label}); menuItemList.add({"id": value.id, "label": value.label});
}); });
@ -162,7 +163,7 @@ class ChromeSafariBrowserMenuItem {
final void Function(String url, String title) action; final void Function(String url, String title) action;
ChromeSafariBrowserMenuItem( ChromeSafariBrowserMenuItem(
{@required this.id, @required this.label, @required this.action}); {required this.id, required this.label, required this.action});
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
return {"id": id, "label": label}; return {"id": id, "label": label};

View File

@ -15,7 +15,7 @@ class ContentBlocker {
///Action associated to the trigger. The action tells to the WebView what to do when the trigger is matched. ///Action associated to the trigger. The action tells to the WebView what to do when the trigger is matched.
ContentBlockerAction action; ContentBlockerAction action;
ContentBlocker({@required this.trigger, @required this.action}); ContentBlocker({required this.trigger, required this.action});
Map<String, Map<String, dynamic>> toMap() { Map<String, Map<String, dynamic>> toMap() {
return {"trigger": trigger.toMap(), "action": action.toMap()}; return {"trigger": trigger.toMap(), "action": action.toMap()};
@ -24,9 +24,9 @@ class ContentBlocker {
static ContentBlocker fromMap(Map<dynamic, Map<dynamic, dynamic>> map) { static ContentBlocker fromMap(Map<dynamic, Map<dynamic, dynamic>> map) {
return ContentBlocker( return ContentBlocker(
trigger: ContentBlockerTrigger.fromMap( trigger: ContentBlockerTrigger.fromMap(
Map<String, dynamic>.from(map["trigger"])), Map<String, dynamic>.from(map["trigger"]!)),
action: ContentBlockerAction.fromMap( action: ContentBlockerAction.fromMap(
Map<String, dynamic>.from(map["action"]))); Map<String, dynamic>.from(map["action"]!)));
} }
} }
@ -36,36 +36,36 @@ class ContentBlocker {
///For example, you can limit the trigger to specific domains or have it not apply when a match is found on a specific domain. ///For example, you can limit the trigger to specific domains or have it not apply when a match is found on a specific domain.
class ContentBlockerTrigger { class ContentBlockerTrigger {
///A regular expression pattern to match the URL against. ///A regular expression pattern to match the URL against.
String urlFilter; late String urlFilter;
///Used only by iOS. A Boolean value. The default value is false. ///Used only by iOS. A Boolean value. The default value is false.
bool urlFilterIsCaseSensitive; late bool urlFilterIsCaseSensitive;
///A list of [ContentBlockerTriggerResourceType] representing the resource types (how the browser intends to use the resource) that the rule should match. ///A list of [ContentBlockerTriggerResourceType] representing the resource types (how the browser intends to use the resource) that the rule should match.
///If not specified, the rule matches all resource types. ///If not specified, the rule matches all resource types.
List<ContentBlockerTriggerResourceType> resourceType; late List<ContentBlockerTriggerResourceType> resourceType;
///A list of strings matched to a URL's domain; limits action to a list of specific domains. ///A list of strings matched to a URL's domain; limits action to a list of specific domains.
///Values must be lowercase ASCII, or punycode for non-ASCII. Add * in front to match domain and subdomains. Can't be used with [ContentBlockerTrigger.unlessDomain]. ///Values must be lowercase ASCII, or punycode for non-ASCII. Add * in front to match domain and subdomains. Can't be used with [ContentBlockerTrigger.unlessDomain].
List<String> ifDomain; late List<String> ifDomain;
///A list of strings matched to a URL's domain; acts on any site except domains in a provided list. ///A list of strings matched to a URL's domain; acts on any site except domains in a provided list.
///Values must be lowercase ASCII, or punycode for non-ASCII. Add * in front to match domain and subdomains. Can't be used with [ContentBlockerTrigger.ifDomain]. ///Values must be lowercase ASCII, or punycode for non-ASCII. Add * in front to match domain and subdomains. Can't be used with [ContentBlockerTrigger.ifDomain].
List<String> unlessDomain; late List<String> unlessDomain;
///A list of [ContentBlockerTriggerLoadType] that can include one of two mutually exclusive values. If not specified, the rule matches all load types. ///A list of [ContentBlockerTriggerLoadType] that can include one of two mutually exclusive values. If not specified, the rule matches all load types.
List<ContentBlockerTriggerLoadType> loadType; late List<ContentBlockerTriggerLoadType> loadType;
///A list of strings matched to the entire main document URL; limits the action to a specific list of URL patterns. ///A list of strings matched to the entire main document URL; limits the action to a specific list of URL patterns.
///Values must be lowercase ASCII, or punycode for non-ASCII. Can't be used with [ContentBlockerTrigger.unlessTopUrl]. ///Values must be lowercase ASCII, or punycode for non-ASCII. Can't be used with [ContentBlockerTrigger.unlessTopUrl].
List<String> ifTopUrl; late List<String> ifTopUrl;
///An array of strings matched to the entire main document URL; acts on any site except URL patterns in provided list. ///An array of strings matched to the entire main document URL; acts on any site except URL patterns in provided list.
///Values must be lowercase ASCII, or punycode for non-ASCII. Can't be used with [ContentBlockerTrigger.ifTopUrl]. ///Values must be lowercase ASCII, or punycode for non-ASCII. Can't be used with [ContentBlockerTrigger.ifTopUrl].
List<String> unlessTopUrl; late List<String> unlessTopUrl;
ContentBlockerTrigger( ContentBlockerTrigger(
{@required String urlFilter, {required String urlFilter,
bool urlFilterIsCaseSensitive = false, bool urlFilterIsCaseSensitive = false,
List<ContentBlockerTriggerResourceType> resourceType = const [], List<ContentBlockerTriggerResourceType> resourceType = const [],
List<String> ifDomain = const [], List<String> ifDomain = const [],
@ -74,7 +74,6 @@ class ContentBlockerTrigger {
List<String> ifTopUrl = const [], List<String> ifTopUrl = const [],
List<String> unlessTopUrl = const []}) { List<String> unlessTopUrl = const []}) {
this.urlFilter = urlFilter; this.urlFilter = urlFilter;
assert(this.urlFilter != null);
this.resourceType = resourceType; this.resourceType = resourceType;
this.urlFilterIsCaseSensitive = urlFilterIsCaseSensitive; this.urlFilterIsCaseSensitive = urlFilterIsCaseSensitive;
this.ifDomain = ifDomain; this.ifDomain = ifDomain;
@ -124,13 +123,19 @@ class ContentBlockerTrigger {
List<String> resourceTypeStringList = List<String> resourceTypeStringList =
List<String>.from(map["resource-type"] ?? []); List<String>.from(map["resource-type"] ?? []);
resourceTypeStringList.forEach((type) { resourceTypeStringList.forEach((typeValue) {
resourceType.add(ContentBlockerTriggerResourceType.fromValue(type)); var type = ContentBlockerTriggerResourceType.fromValue(typeValue);
if (type != null) {
resourceType.add(type);
}
}); });
List<String> loadTypeStringList = List<String>.from(map["load-type"] ?? []); List<String> loadTypeStringList = List<String>.from(map["load-type"] ?? []);
loadTypeStringList.forEach((type) { loadTypeStringList.forEach((typeValue) {
loadType.add(ContentBlockerTriggerLoadType.fromValue(type)); var type = ContentBlockerTriggerLoadType.fromValue(typeValue);
if (type != null) {
loadType.add(type);
}
}); });
return ContentBlockerTrigger( return ContentBlockerTrigger(
@ -152,16 +157,15 @@ class ContentBlockerTrigger {
///Group the rules with similar actions together to improve performance. ///Group the rules with similar actions together to improve performance.
class ContentBlockerAction { class ContentBlockerAction {
///Type of the action. ///Type of the action.
ContentBlockerActionType type; late ContentBlockerActionType type;
///If the action type is [ContentBlockerActionType.CSS_DISPLAY_NONE], then also the [selector] property is required, otherwise it is ignored. ///If the action type is [ContentBlockerActionType.CSS_DISPLAY_NONE], then also the [selector] property is required, otherwise it is ignored.
///It specify a string that defines a selector list. Use CSS identifiers as the individual selector values, separated by commas. ///It specify a string that defines a selector list. Use CSS identifiers as the individual selector values, separated by commas.
String selector; String? selector;
ContentBlockerAction( ContentBlockerAction(
{@required ContentBlockerActionType type, String selector}) { {required ContentBlockerActionType type, String? selector}) {
this.type = type; this.type = type;
assert(this.type != null);
if (this.type == ContentBlockerActionType.CSS_DISPLAY_NONE) { if (this.type == ContentBlockerActionType.CSS_DISPLAY_NONE) {
assert(selector != null); assert(selector != null);
} }
@ -183,7 +187,7 @@ class ContentBlockerAction {
static ContentBlockerAction fromMap(Map<String, dynamic> map) { static ContentBlockerAction fromMap(Map<String, dynamic> map) {
return ContentBlockerAction( return ContentBlockerAction(
type: ContentBlockerActionType.fromValue(map["type"]), type: ContentBlockerActionType.fromValue(map["type"])!,
selector: map["selector"]); selector: map["selector"]);
} }
} }

View File

@ -10,20 +10,20 @@ class ContextMenu {
///Event fired when the context menu for this WebView is being built. ///Event fired when the context menu for this WebView is being built.
/// ///
///[hitTestResult] represents the hit result for hitting an HTML elements. ///[hitTestResult] represents the hit result for hitting an HTML elements.
final void Function(InAppWebViewHitTestResult hitTestResult) final void Function(InAppWebViewHitTestResult hitTestResult)?
onCreateContextMenu; onCreateContextMenu;
///Event fired when the context menu for this WebView is being hidden. ///Event fired when the context menu for this WebView is being hidden.
final void Function() onHideContextMenu; final void Function()? onHideContextMenu;
///Event fired when a context menu item has been clicked. ///Event fired when a context menu item has been clicked.
/// ///
///[contextMenuItemClicked] represents the [ContextMenuItem] clicked. ///[contextMenuItemClicked] represents the [ContextMenuItem] clicked.
final void Function(ContextMenuItem contextMenuItemClicked) final void Function(ContextMenuItem contextMenuItemClicked)?
onContextMenuActionItemClicked; onContextMenuActionItemClicked;
///Context menu options. ///Context menu options.
final ContextMenuOptions options; final ContextMenuOptions? options;
///List of the custom [ContextMenuItem]. ///List of the custom [ContextMenuItem].
final List<ContextMenuItem> menuItems; final List<ContextMenuItem> menuItems;
@ -33,12 +33,11 @@ class ContextMenu {
this.onCreateContextMenu, this.onCreateContextMenu,
this.onHideContextMenu, this.onHideContextMenu,
this.options, this.options,
this.onContextMenuActionItemClicked}) this.onContextMenuActionItemClicked});
: assert(menuItems != null);
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
return { return {
"menuItems": menuItems.map((menuItem) => menuItem?.toMap()).toList(), "menuItems": menuItems.map((menuItem) => menuItem.toMap()).toList(),
"options": options?.toMap() "options": options?.toMap()
}; };
} }
@ -56,21 +55,21 @@ class ContextMenu {
///Class that represent an item of the [ContextMenu]. ///Class that represent an item of the [ContextMenu].
class ContextMenuItem { class ContextMenuItem {
///Android menu item ID. ///Android menu item ID.
int androidId; int? androidId;
///iOS menu item ID. ///iOS menu item ID.
String iosId; String? iosId;
///Menu item title. ///Menu item title.
String title; String title;
///Menu item action that will be called when an user clicks on it. ///Menu item action that will be called when an user clicks on it.
Function() action; Function()? action;
ContextMenuItem( ContextMenuItem(
{@required this.androidId, {this.androidId,
@required this.iosId, this.iosId,
@required this.title, required this.title,
this.action}); this.action});
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {

View File

@ -1,6 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'types.dart'; import 'types.dart';
@ -11,19 +10,19 @@ import 'types.dart';
/// ///
///**NOTE for iOS**: available from iOS 11.0+. ///**NOTE for iOS**: available from iOS 11.0+.
class CookieManager { class CookieManager {
static CookieManager _instance; static CookieManager? _instance;
static const MethodChannel _channel = const MethodChannel( static const MethodChannel _channel = const MethodChannel(
'com.pichillilorenzo/flutter_inappwebview_cookiemanager'); 'com.pichillilorenzo/flutter_inappwebview_cookiemanager');
///Gets the cookie manager shared instance. ///Gets the cookie manager shared instance.
static CookieManager instance() { static CookieManager instance() {
return (_instance != null) ? _instance : _init(); return (_instance != null) ? _instance! : _init();
} }
static CookieManager _init() { static CookieManager _init() {
_channel.setMethodCallHandler(_handleMethod); _channel.setMethodCallHandler(_handleMethod);
_instance = CookieManager(); _instance = CookieManager();
return _instance; return _instance!;
} }
static Future<dynamic> _handleMethod(MethodCall call) async {} static Future<dynamic> _handleMethod(MethodCall call) async {}
@ -33,23 +32,23 @@ class CookieManager {
///The default value of [path] is `"/"`. ///The default value of [path] is `"/"`.
///If [domain] is `null`, its default value will be the domain name of [url]. ///If [domain] is `null`, its default value will be the domain name of [url].
Future<void> setCookie( Future<void> setCookie(
{@required String url, {required String url,
@required String name, required String name,
@required String value, required String value,
String domain, String? domain,
String path = "/", String path = "/",
int expiresDate, int? expiresDate,
int maxAge, int? maxAge,
bool isSecure, bool? isSecure,
bool isHttpOnly, bool? isHttpOnly,
HTTPCookieSameSitePolicy sameSite}) async { HTTPCookieSameSitePolicy? sameSite}) async {
if (domain == null) domain = _getDomainName(url); if (domain == null) domain = _getDomainName(url);
assert(url != null && url.isNotEmpty); assert(url.isNotEmpty);
assert(name != null && name.isNotEmpty); assert(name.isNotEmpty);
assert(value != null && value.isNotEmpty); assert(value.isNotEmpty);
assert(domain != null && domain.isNotEmpty); assert(domain.isNotEmpty);
assert(path != null && path.isNotEmpty); assert(path.isNotEmpty);
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url); args.putIfAbsent('url', () => url);
@ -67,8 +66,8 @@ class CookieManager {
} }
///Gets all the cookies for the given [url]. ///Gets all the cookies for the given [url].
Future<List<Cookie>> getCookies({@required String url}) async { Future<List<Cookie>> getCookies({required String url}) async {
assert(url != null && url.isNotEmpty); assert(url.isNotEmpty);
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url); args.putIfAbsent('url', () => url);
@ -94,10 +93,10 @@ class CookieManager {
} }
///Gets a cookie by its [name] for the given [url]. ///Gets a cookie by its [name] for the given [url].
Future<Cookie> getCookie( Future<Cookie?> getCookie(
{@required String url, @required String name}) async { {required String url, required String name}) async {
assert(url != null && url.isNotEmpty); assert(url.isNotEmpty);
assert(name != null && name.isNotEmpty); assert(name.isNotEmpty);
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url); args.putIfAbsent('url', () => url);
@ -126,16 +125,16 @@ class CookieManager {
///The default value of [path] is `"/"`. ///The default value of [path] is `"/"`.
///If [domain] is `null` or empty, its default value will be the domain name of [url]. ///If [domain] is `null` or empty, its default value will be the domain name of [url].
Future<void> deleteCookie( Future<void> deleteCookie(
{@required String url, {required String url,
@required String name, required String name,
String domain = "", String domain = "",
String path = "/"}) async { String path = "/"}) async {
if (domain == null || domain.isEmpty) domain = _getDomainName(url); if (domain.isEmpty) domain = _getDomainName(url);
assert(url != null && url.isNotEmpty); assert(url.isNotEmpty);
assert(name != null && name.isNotEmpty); assert(name.isNotEmpty);
assert(domain != null && url.isNotEmpty); assert(url.isNotEmpty);
assert(path != null && url.isNotEmpty); assert(url.isNotEmpty);
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url); args.putIfAbsent('url', () => url);
@ -150,12 +149,12 @@ class CookieManager {
///The default value of [path] is `"/"`. ///The default value of [path] is `"/"`.
///If [domain] is `null` or empty, its default value will be the domain name of [url]. ///If [domain] is `null` or empty, its default value will be the domain name of [url].
Future<void> deleteCookies( Future<void> deleteCookies(
{@required String url, String domain = "", String path = "/"}) async { {required String url, String domain = "", String path = "/"}) async {
if (domain == null || domain.isEmpty) domain = _getDomainName(url); if (domain.isEmpty) domain = _getDomainName(url);
assert(url != null && url.isNotEmpty); assert(url.isNotEmpty);
assert(domain != null && url.isNotEmpty); assert(url.isNotEmpty);
assert(path != null && url.isNotEmpty); assert(url.isNotEmpty);
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url); args.putIfAbsent('url', () => url);
@ -173,6 +172,7 @@ class CookieManager {
String _getDomainName(String url) { String _getDomainName(String url) {
Uri uri = Uri.parse(url); Uri uri = Uri.parse(url);
String domain = uri.host; String domain = uri.host;
// ignore: unnecessary_null_comparison
if (domain == null) return ""; if (domain == null) return "";
return domain.startsWith("www.") ? domain.substring(4) : domain; return domain.startsWith("www.") ? domain.substring(4) : domain;
} }

View File

@ -12,16 +12,16 @@ import 'in_app_webview_controller.dart';
/// ///
///Remember to dispose it when you don't need it anymore. ///Remember to dispose it when you don't need it anymore.
class HeadlessInAppWebView implements WebView { class HeadlessInAppWebView implements WebView {
String uuid; late String uuid;
bool _isDisposed = true; bool _isDisposed = true;
static const MethodChannel _sharedChannel = static const MethodChannel _sharedChannel =
const MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview'); const MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview');
///WebView Controller that can be used to access the [InAppWebViewController] API. ///WebView Controller that can be used to access the [InAppWebViewController] API.
InAppWebViewController webViewController; late InAppWebViewController webViewController;
///The window id of a [CreateWindowRequest.windowId]. ///The window id of a [CreateWindowRequest.windowId].
final int windowId; final int? windowId;
HeadlessInAppWebView( HeadlessInAppWebView(
{this.windowId, {this.windowId,
@ -89,7 +89,9 @@ class HeadlessInAppWebView implements WebView {
Future<dynamic> handleMethod(MethodCall call) async { Future<dynamic> handleMethod(MethodCall call) async {
switch (call.method) { switch (call.method) {
case "onHeadlessWebViewCreated": case "onHeadlessWebViewCreated":
onWebViewCreated(webViewController); if (onWebViewCreated != null) {
onWebViewCreated!(webViewController);
}
break; break;
default: default:
return webViewController.handleMethod(call); return webViewController.handleMethod(call);
@ -107,12 +109,13 @@ class HeadlessInAppWebView implements WebView {
args.putIfAbsent( args.putIfAbsent(
'params', 'params',
() => <String, dynamic>{ () => <String, dynamic>{
'initialUrl': '${Uri.parse(this.initialUrl)}', 'initialUrl': this.initialUrl != null ? '${Uri.parse(this.initialUrl!)}' : '',
'initialFile': this.initialFile, 'initialFile': this.initialFile,
'initialData': this.initialData?.toMap(), 'initialData': this.initialData?.toMap(),
'initialHeaders': this.initialHeaders, 'initialHeaders': this.initialHeaders,
'initialOptions': this.initialOptions?.toMap() ?? {}, 'initialOptions': this.initialOptions?.toMap() ?? {},
'contextMenu': this.contextMenu?.toMap() ?? {} 'contextMenu': this.contextMenu?.toMap() ?? {},
'windowId': this.windowId
}); });
await _sharedChannel.invokeMethod('createHeadlessWebView', args); await _sharedChannel.invokeMethod('createHeadlessWebView', args);
} }
@ -129,242 +132,242 @@ class HeadlessInAppWebView implements WebView {
} }
@override @override
final void Function(InAppWebViewController controller) final void Function(InAppWebViewController controller)?
androidOnGeolocationPermissionsHidePrompt; androidOnGeolocationPermissionsHidePrompt;
@override @override
final Future<GeolocationPermissionShowPromptResponse> Function( final Future<GeolocationPermissionShowPromptResponse?> Function(
InAppWebViewController controller, String origin) InAppWebViewController controller, String origin)?
androidOnGeolocationPermissionsShowPrompt; androidOnGeolocationPermissionsShowPrompt;
@override @override
final Future<PermissionRequestResponse> Function( final Future<PermissionRequestResponse?> Function(
InAppWebViewController controller, InAppWebViewController controller,
String origin, String origin,
List<String> resources) androidOnPermissionRequest; List<String> resources)? androidOnPermissionRequest;
@override @override
final Future<SafeBrowsingResponse> Function(InAppWebViewController controller, final Future<SafeBrowsingResponse?> Function(InAppWebViewController controller,
String url, SafeBrowsingThreat threatType) androidOnSafeBrowsingHit; String url, SafeBrowsingThreat? threatType)? androidOnSafeBrowsingHit;
@override @override
final InAppWebViewInitialData initialData; final InAppWebViewInitialData? initialData;
@override @override
final String initialFile; final String? initialFile;
@override @override
final Map<String, String> initialHeaders; final Map<String, String>? initialHeaders;
@override @override
final InAppWebViewGroupOptions initialOptions; final InAppWebViewGroupOptions? initialOptions;
@override @override
final ContextMenu contextMenu; final ContextMenu? contextMenu;
@override @override
final String initialUrl; final String? initialUrl;
@override @override
final void Function(InAppWebViewController controller, String url) final void Function(InAppWebViewController controller, String? url)?
onPageCommitVisible; onPageCommitVisible;
@override @override
final void Function(InAppWebViewController controller, String title) final void Function(InAppWebViewController controller, String? title)?
onTitleChanged; onTitleChanged;
@override @override
final void Function(InAppWebViewController controller) final void Function(InAppWebViewController controller)?
iosOnDidReceiveServerRedirectForProvisionalNavigation; iosOnDidReceiveServerRedirectForProvisionalNavigation;
@override @override
final void Function(InAppWebViewController controller) final void Function(InAppWebViewController controller)?
iosOnWebContentProcessDidTerminate; iosOnWebContentProcessDidTerminate;
@override @override
final Future<AjaxRequestAction> Function( final Future<AjaxRequestAction> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest) InAppWebViewController controller, AjaxRequest ajaxRequest)?
onAjaxProgress; onAjaxProgress;
@override @override
final Future<AjaxRequestAction> Function( final Future<AjaxRequestAction?> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest) InAppWebViewController controller, AjaxRequest ajaxRequest)?
onAjaxReadyStateChange; onAjaxReadyStateChange;
@override @override
final void Function( final void Function(
InAppWebViewController controller, ConsoleMessage consoleMessage) InAppWebViewController controller, ConsoleMessage consoleMessage)?
onConsoleMessage; onConsoleMessage;
@override @override
final Future<bool> Function(InAppWebViewController controller, final Future<bool?> Function(InAppWebViewController controller,
CreateWindowRequest createWindowRequest) onCreateWindow; CreateWindowRequest createWindowRequest)? onCreateWindow;
@override @override
final void Function(InAppWebViewController controller) onCloseWindow; final void Function(InAppWebViewController controller)? onCloseWindow;
@override @override
final void Function(InAppWebViewController controller) onWindowFocus; final void Function(InAppWebViewController controller)? onWindowFocus;
@override @override
final void Function(InAppWebViewController controller) onWindowBlur; final void Function(InAppWebViewController controller)? onWindowBlur;
@override @override
final void Function(InAppWebViewController controller) androidOnRequestFocus; final void Function(InAppWebViewController controller)? androidOnRequestFocus;
@override @override
final void Function(InAppWebViewController controller, String url) final void Function(InAppWebViewController controller, String url)?
onDownloadStart; onDownloadStart;
@override @override
final void Function(InAppWebViewController controller, int activeMatchOrdinal, final void Function(InAppWebViewController controller, int activeMatchOrdinal,
int numberOfMatches, bool isDoneCounting) onFindResultReceived; int numberOfMatches, bool isDoneCounting)? onFindResultReceived;
@override @override
final Future<JsAlertResponse> Function( final Future<JsAlertResponse?> Function(
InAppWebViewController controller, JsAlertRequest jsAlertRequest) InAppWebViewController controller, JsAlertRequest jsAlertRequest)?
onJsAlert; onJsAlert;
@override @override
final Future<JsConfirmResponse> Function( final Future<JsConfirmResponse?> Function(
InAppWebViewController controller, JsConfirmRequest jsConfirmRequest) InAppWebViewController controller, JsConfirmRequest jsConfirmRequest)?
onJsConfirm; onJsConfirm;
@override @override
final Future<JsPromptResponse> Function( final Future<JsPromptResponse?> Function(
InAppWebViewController controller, JsPromptRequest jsPromptRequest) InAppWebViewController controller, JsPromptRequest jsPromptRequest)?
onJsPrompt; onJsPrompt;
@override @override
final void Function(InAppWebViewController controller, String url, int code, final void Function(InAppWebViewController controller, String? url, int code,
String message) onLoadError; String message)? onLoadError;
@override @override
final void Function(InAppWebViewController controller, String url, final void Function(InAppWebViewController controller, String? url,
int statusCode, String description) onLoadHttpError; int statusCode, String description)? onLoadHttpError;
@override @override
final void Function( final void Function(
InAppWebViewController controller, LoadedResource resource) InAppWebViewController controller, LoadedResource resource)?
onLoadResource; onLoadResource;
@override @override
final Future<CustomSchemeResponse> Function( final Future<CustomSchemeResponse?> Function(
InAppWebViewController controller, String scheme, String url) InAppWebViewController controller, String scheme, String url)?
onLoadResourceCustomScheme; onLoadResourceCustomScheme;
@override @override
final void Function(InAppWebViewController controller, String url) final void Function(InAppWebViewController controller, String? url)?
onLoadStart; onLoadStart;
@override @override
final void Function(InAppWebViewController controller, String url) onLoadStop; final void Function(InAppWebViewController controller, String? url)? onLoadStop;
@override @override
final void Function(InAppWebViewController controller, final void Function(InAppWebViewController controller,
InAppWebViewHitTestResult hitTestResult) onLongPressHitTestResult; InAppWebViewHitTestResult hitTestResult)? onLongPressHitTestResult;
@override @override
final void Function(InAppWebViewController controller, String url) onPrint; final void Function(InAppWebViewController controller, String? url)? onPrint;
@override @override
final void Function(InAppWebViewController controller, int progress) final void Function(InAppWebViewController controller, int progress)?
onProgressChanged; onProgressChanged;
@override @override
final Future<ClientCertResponse> Function( final Future<ClientCertResponse?> Function(
InAppWebViewController controller, ClientCertChallenge challenge) InAppWebViewController controller, ClientCertChallenge challenge)?
onReceivedClientCertRequest; onReceivedClientCertRequest;
@override @override
final Future<HttpAuthResponse> Function( final Future<HttpAuthResponse?> Function(
InAppWebViewController controller, HttpAuthChallenge challenge) InAppWebViewController controller, HttpAuthChallenge challenge)?
onReceivedHttpAuthRequest; onReceivedHttpAuthRequest;
@override @override
final Future<ServerTrustAuthResponse> Function( final Future<ServerTrustAuthResponse?> Function(
InAppWebViewController controller, ServerTrustChallenge challenge) InAppWebViewController controller, ServerTrustChallenge challenge)?
onReceivedServerTrustAuthRequest; onReceivedServerTrustAuthRequest;
@override @override
final void Function(InAppWebViewController controller, int x, int y) final void Function(InAppWebViewController controller, int x, int y)?
onScrollChanged; onScrollChanged;
@override @override
final void Function( final void Function(
InAppWebViewController controller, String url, bool androidIsReload) InAppWebViewController controller, String? url, bool? androidIsReload)?
onUpdateVisitedHistory; onUpdateVisitedHistory;
@override @override
final void Function(InAppWebViewController controller) onWebViewCreated; final void Function(InAppWebViewController controller)? onWebViewCreated;
@override @override
final Future<AjaxRequest> Function( final Future<AjaxRequest?> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest) InAppWebViewController controller, AjaxRequest ajaxRequest)?
shouldInterceptAjaxRequest; shouldInterceptAjaxRequest;
@override @override
final Future<FetchRequest> Function( final Future<FetchRequest?> Function(
InAppWebViewController controller, FetchRequest fetchRequest) InAppWebViewController controller, FetchRequest fetchRequest)?
shouldInterceptFetchRequest; shouldInterceptFetchRequest;
@override @override
final Future<ShouldOverrideUrlLoadingAction> Function( final Future<ShouldOverrideUrlLoadingAction?> Function(
InAppWebViewController controller, InAppWebViewController controller,
ShouldOverrideUrlLoadingRequest shouldOverrideUrlLoadingRequest) ShouldOverrideUrlLoadingRequest shouldOverrideUrlLoadingRequest)?
shouldOverrideUrlLoading; shouldOverrideUrlLoading;
@override @override
final void Function(InAppWebViewController controller) onEnterFullscreen; final void Function(InAppWebViewController controller)? onEnterFullscreen;
@override @override
final void Function(InAppWebViewController controller) onExitFullscreen; final void Function(InAppWebViewController controller)? onExitFullscreen;
@override @override
final Future<WebResourceResponse> Function( final Future<WebResourceResponse?> Function(
InAppWebViewController controller, WebResourceRequest request) InAppWebViewController controller, WebResourceRequest request)?
androidShouldInterceptRequest; androidShouldInterceptRequest;
@override @override
final Future<WebViewRenderProcessAction> Function( final Future<WebViewRenderProcessAction?> Function(
InAppWebViewController controller, String url) InAppWebViewController controller, String? url)?
androidOnRenderProcessUnresponsive; androidOnRenderProcessUnresponsive;
@override @override
final Future<WebViewRenderProcessAction> Function( final Future<WebViewRenderProcessAction?> Function(
InAppWebViewController controller, String url) InAppWebViewController controller, String? url)?
androidOnRenderProcessResponsive; androidOnRenderProcessResponsive;
@override @override
final void Function( final void Function(
InAppWebViewController controller, RenderProcessGoneDetail detail) InAppWebViewController controller, RenderProcessGoneDetail detail)?
androidOnRenderProcessGone; androidOnRenderProcessGone;
@override @override
final Future<FormResubmissionAction> Function( final Future<FormResubmissionAction?> Function(
InAppWebViewController controller, String url) androidOnFormResubmission; InAppWebViewController controller, String? url)? androidOnFormResubmission;
@override @override
final void Function( final void Function(
InAppWebViewController controller, double oldScale, double newScale) InAppWebViewController controller, double oldScale, double newScale)?
androidOnScaleChanged; androidOnScaleChanged;
@override @override
final void Function(InAppWebViewController controller, Uint8List icon) final void Function(InAppWebViewController controller, Uint8List icon)?
androidOnReceivedIcon; androidOnReceivedIcon;
@override @override
final void Function( final void Function(
InAppWebViewController controller, String url, bool precomposed) InAppWebViewController controller, String url, bool precomposed)?
androidOnReceivedTouchIconUrl; androidOnReceivedTouchIconUrl;
@override @override
final Future<JsBeforeUnloadResponse> Function( final Future<JsBeforeUnloadResponse?> Function(
InAppWebViewController controller, InAppWebViewController controller,
JsBeforeUnloadRequest jsBeforeUnloadRequest) androidOnJsBeforeUnload; JsBeforeUnloadRequest jsBeforeUnloadRequest)? androidOnJsBeforeUnload;
@override @override
final void Function( final void Function(
InAppWebViewController controller, LoginRequest loginRequest) InAppWebViewController controller, LoginRequest loginRequest)?
androidOnReceivedLoginRequest; androidOnReceivedLoginRequest;
} }

View File

@ -1,7 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/foundation.dart';
import 'types.dart'; import 'types.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -11,19 +9,19 @@ import 'package:flutter/services.dart';
///[WebViewDatabase](https://developer.android.com/reference/android/webkit/WebViewDatabase) ///[WebViewDatabase](https://developer.android.com/reference/android/webkit/WebViewDatabase)
///doesn't offer the same functionalities as iOS `URLCredentialStorage`. ///doesn't offer the same functionalities as iOS `URLCredentialStorage`.
class HttpAuthCredentialDatabase { class HttpAuthCredentialDatabase {
static HttpAuthCredentialDatabase _instance; static HttpAuthCredentialDatabase? _instance;
static const MethodChannel _channel = const MethodChannel( static const MethodChannel _channel = const MethodChannel(
'com.pichillilorenzo/flutter_inappwebview_credential_database'); 'com.pichillilorenzo/flutter_inappwebview_credential_database');
///Gets the database shared instance. ///Gets the database shared instance.
static HttpAuthCredentialDatabase instance() { static HttpAuthCredentialDatabase instance() {
return (_instance != null) ? _instance : _init(); return (_instance != null) ? _instance! : _init();
} }
static HttpAuthCredentialDatabase _init() { static HttpAuthCredentialDatabase _init() {
_channel.setMethodCallHandler(_handleMethod); _channel.setMethodCallHandler(_handleMethod);
_instance = HttpAuthCredentialDatabase(); _instance = HttpAuthCredentialDatabase();
return _instance; return _instance!;
} }
static Future<dynamic> _handleMethod(MethodCall call) async {} static Future<dynamic> _handleMethod(MethodCall call) async {}
@ -59,7 +57,7 @@ class HttpAuthCredentialDatabase {
///Gets all the HTTP auth credentials saved for that [protectionSpace]. ///Gets all the HTTP auth credentials saved for that [protectionSpace].
Future<List<HttpAuthCredential>> getHttpAuthCredentials( Future<List<HttpAuthCredential>> getHttpAuthCredentials(
{@required ProtectionSpace protectionSpace}) async { {required ProtectionSpace protectionSpace}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("host", () => protectionSpace.host); args.putIfAbsent("host", () => protectionSpace.host);
args.putIfAbsent("protocol", () => protectionSpace.protocol); args.putIfAbsent("protocol", () => protectionSpace.protocol);
@ -77,8 +75,8 @@ class HttpAuthCredentialDatabase {
///Saves an HTTP auth [credential] for that [protectionSpace]. ///Saves an HTTP auth [credential] for that [protectionSpace].
Future<void> setHttpAuthCredential( Future<void> setHttpAuthCredential(
{@required ProtectionSpace protectionSpace, {required ProtectionSpace protectionSpace,
@required HttpAuthCredential credential}) async { required HttpAuthCredential credential}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("host", () => protectionSpace.host); args.putIfAbsent("host", () => protectionSpace.host);
args.putIfAbsent("protocol", () => protectionSpace.protocol); args.putIfAbsent("protocol", () => protectionSpace.protocol);
@ -91,8 +89,8 @@ class HttpAuthCredentialDatabase {
///Removes an HTTP auth [credential] for that [protectionSpace]. ///Removes an HTTP auth [credential] for that [protectionSpace].
Future<void> removeHttpAuthCredential( Future<void> removeHttpAuthCredential(
{@required ProtectionSpace protectionSpace, {required ProtectionSpace protectionSpace,
@required HttpAuthCredential credential}) async { required HttpAuthCredential credential}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("host", () => protectionSpace.host); args.putIfAbsent("host", () => protectionSpace.host);
args.putIfAbsent("protocol", () => protectionSpace.protocol); args.putIfAbsent("protocol", () => protectionSpace.protocol);
@ -105,7 +103,7 @@ class HttpAuthCredentialDatabase {
///Removes all the HTTP auth credentials saved for that [protectionSpace]. ///Removes all the HTTP auth credentials saved for that [protectionSpace].
Future<void> removeHttpAuthCredentials( Future<void> removeHttpAuthCredentials(
{@required ProtectionSpace protectionSpace}) async { {required ProtectionSpace protectionSpace}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("host", () => protectionSpace.host); args.putIfAbsent("host", () => protectionSpace.host);
args.putIfAbsent("protocol", () => protectionSpace.protocol); args.putIfAbsent("protocol", () => protectionSpace.protocol);

View File

@ -2,7 +2,6 @@ import 'dart:async';
import 'dart:collection'; import 'dart:collection';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'context_menu.dart'; import 'context_menu.dart';
@ -15,23 +14,23 @@ import 'types.dart';
///The [webViewController] field can be used to access the [InAppWebViewController] API. ///The [webViewController] field can be used to access the [InAppWebViewController] API.
class InAppBrowser { class InAppBrowser {
///Browser's UUID. ///Browser's UUID.
String uuid; late String uuid;
///Context menu used by the browser. It should be set before opening the browser. ///Context menu used by the browser. It should be set before opening the browser.
ContextMenu contextMenu; ContextMenu? contextMenu;
Map<String, JavaScriptHandlerCallback> javaScriptHandlersMap = Map<String, JavaScriptHandlerCallback> javaScriptHandlersMap =
HashMap<String, JavaScriptHandlerCallback>(); HashMap<String, JavaScriptHandlerCallback>();
bool _isOpened = false; bool _isOpened = false;
MethodChannel _channel; late MethodChannel _channel;
static const MethodChannel _sharedChannel = static const MethodChannel _sharedChannel =
const MethodChannel('com.pichillilorenzo/flutter_inappbrowser'); const MethodChannel('com.pichillilorenzo/flutter_inappbrowser');
/// WebView Controller that can be used to access the [InAppWebViewController] API. /// WebView Controller that can be used to access the [InAppWebViewController] API.
InAppWebViewController webViewController; late InAppWebViewController webViewController;
///The window id of a [CreateWindowRequest.windowId]. ///The window id of a [CreateWindowRequest.windowId].
final int windowId; final int? windowId;
/// ///
InAppBrowser({this.windowId}) { InAppBrowser({this.windowId}) {
@ -67,10 +66,10 @@ class InAppBrowser {
/// ///
///[options]: Options for the [InAppBrowser]. ///[options]: Options for the [InAppBrowser].
Future<void> openUrl( Future<void> openUrl(
{@required String url, {required String url,
Map<String, String> headers = const {}, Map<String, String> headers = const {},
InAppBrowserClassOptions options}) async { InAppBrowserClassOptions? options}) async {
assert(url != null && url.isNotEmpty); assert(url.isNotEmpty);
this.throwIsAlreadyOpened(message: 'Cannot open $url!'); this.throwIsAlreadyOpened(message: 'Cannot open $url!');
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
@ -117,10 +116,10 @@ class InAppBrowser {
/// ///
///[options]: Options for the [InAppBrowser]. ///[options]: Options for the [InAppBrowser].
Future<void> openFile( Future<void> openFile(
{@required String assetFilePath, {required String assetFilePath,
Map<String, String> headers = const {}, Map<String, String> headers = const {},
InAppBrowserClassOptions options}) async { InAppBrowserClassOptions? options}) async {
assert(assetFilePath != null && assetFilePath.isNotEmpty); assert(assetFilePath.isNotEmpty);
this.throwIsAlreadyOpened(message: 'Cannot open $assetFilePath!'); this.throwIsAlreadyOpened(message: 'Cannot open $assetFilePath!');
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
@ -143,14 +142,12 @@ class InAppBrowser {
/// ///
///The [options] parameter specifies the options for the [InAppBrowser]. ///The [options] parameter specifies the options for the [InAppBrowser].
Future<void> openData( Future<void> openData(
{@required String data, {required String data,
String mimeType = "text/html", String mimeType = "text/html",
String encoding = "utf8", String encoding = "utf8",
String baseUrl = "about:blank", String baseUrl = "about:blank",
String androidHistoryUrl = "about:blank", String androidHistoryUrl = "about:blank",
InAppBrowserClassOptions options}) async { InAppBrowserClassOptions? options}) async {
assert(data != null);
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid); args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('options', () => options?.toMap() ?? {}); args.putIfAbsent('options', () => options?.toMap() ?? {});
@ -165,8 +162,8 @@ class InAppBrowser {
} }
///This is a static method that opens an [url] in the system browser. You wont be able to use the [InAppBrowser] methods here! ///This is a static method that opens an [url] in the system browser. You wont be able to use the [InAppBrowser] methods here!
static Future<void> openWithSystemBrowser({@required String url}) async { static Future<void> openWithSystemBrowser({required String url}) async {
assert(url != null && url.isNotEmpty); assert(url.isNotEmpty);
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url); args.putIfAbsent('url', () => url);
return await _sharedChannel.invokeMethod('openWithSystemBrowser', args); return await _sharedChannel.invokeMethod('openWithSystemBrowser', args);
@ -202,24 +199,24 @@ class InAppBrowser {
} }
///Sets the [InAppBrowser] options with the new [options] and evaluates them. ///Sets the [InAppBrowser] options with the new [options] and evaluates them.
Future<void> setOptions({@required InAppBrowserClassOptions options}) async { Future<void> setOptions({required InAppBrowserClassOptions options}) async {
this.throwIsNotOpened(); this.throwIsNotOpened();
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('options', () => options?.toMap() ?? {}); args.putIfAbsent('options', () => options.toMap());
await _channel.invokeMethod('setOptions', args); await _channel.invokeMethod('setOptions', args);
} }
///Gets the current [InAppBrowser] options. Returns `null` if it wasn't able to get them. ///Gets the current [InAppBrowser] options. Returns `null` if it wasn't able to get them.
Future<InAppBrowserClassOptions> getOptions() async { Future<InAppBrowserClassOptions?> getOptions() async {
this.throwIsNotOpened(); this.throwIsNotOpened();
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
Map<dynamic, dynamic> options = Map<dynamic, dynamic>? options =
await _channel.invokeMethod('getOptions', args); await _channel.invokeMethod('getOptions', args);
if (options != null) { if (options != null) {
options = options.cast<String, dynamic>(); options = options.cast<String, dynamic>();
return InAppBrowserClassOptions.fromMap(options); return InAppBrowserClassOptions.fromMap(options as Map<String, dynamic>);
} }
return null; return null;
@ -241,21 +238,21 @@ class InAppBrowser {
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onPageStarted(android.webkit.WebView,%20java.lang.String,%20android.graphics.Bitmap) ///**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 ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455621-webview
void onLoadStart(String url) {} void onLoadStart(String? url) {}
///Event fired when the [InAppBrowser] finishes loading an [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 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 ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455629-webview
void onLoadStop(String url) {} void onLoadStop(String? url) {}
///Event fired when the [InAppBrowser] encounters an error loading an [url]. ///Event fired when the [InAppBrowser] encounters an error loading an [url].
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedError(android.webkit.WebView,%20int,%20java.lang.String,%20java.lang.String) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedError(android.webkit.WebView,%20int,%20java.lang.String,%20java.lang.String)
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455623-webview ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455623-webview
void onLoadError(String url, int code, String message) {} void onLoadError(String? url, int code, String message) {}
///Event fired when the [InAppBrowser] main page receives an HTTP error. ///Event fired when the [InAppBrowser] main page receives an HTTP error.
/// ///
@ -270,7 +267,7 @@ class InAppBrowser {
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedHttpError(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20android.webkit.WebResourceResponse) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedHttpError(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20android.webkit.WebResourceResponse)
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview
void onLoadHttpError(String url, int statusCode, String description) {} void onLoadHttpError(String? url, int statusCode, String description) {}
///Event fired when the current [progress] (range 0-100) of loading a page is changed. ///Event fired when the current [progress] (range 0-100) of loading a page is changed.
/// ///
@ -297,8 +294,7 @@ class InAppBrowser {
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#shouldOverrideUrlLoading(android.webkit.WebView,%20java.lang.String) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#shouldOverrideUrlLoading(android.webkit.WebView,%20java.lang.String)
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455641-webview ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455641-webview
// ignore: missing_return Future<ShouldOverrideUrlLoadingAction?>? shouldOverrideUrlLoading(
Future<ShouldOverrideUrlLoadingAction> shouldOverrideUrlLoading(
ShouldOverrideUrlLoadingRequest shouldOverrideUrlLoadingRequest) {} ShouldOverrideUrlLoadingRequest shouldOverrideUrlLoadingRequest) {}
///Event fired when the [InAppBrowser] webview loads a resource. ///Event fired when the [InAppBrowser] webview loads a resource.
@ -335,8 +331,7 @@ class InAppBrowser {
///[url] represents the url of the request. ///[url] represents the url of the request.
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkurlschemehandler ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkurlschemehandler
// ignore: missing_return Future<CustomSchemeResponse?>? onLoadResourceCustomScheme(
Future<CustomSchemeResponse> onLoadResourceCustomScheme(
String scheme, String url) {} String scheme, String url) {}
///Event fired when the [InAppBrowser] webview requests the host application to create a new window, ///Event fired when the [InAppBrowser] webview requests the host application to create a new window,
@ -369,8 +364,7 @@ class InAppBrowser {
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onCreateWindow(android.webkit.WebView,%20boolean,%20boolean,%20android.os.Message) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onCreateWindow(android.webkit.WebView,%20boolean,%20boolean,%20android.os.Message)
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1536907-webview ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1536907-webview
// ignore: missing_return Future<bool?>? onCreateWindow(CreateWindowRequest createWindowRequest) {}
Future<bool> onCreateWindow(CreateWindowRequest createWindowRequest) {}
///Event fired when the host application should close the given WebView and remove it from the view system if necessary. ///Event fired when the host application should close the given WebView and remove it from the view system if necessary.
///At this point, WebCore has stopped any loading in this window and has removed any cross-scripting ability in javascript. ///At this point, WebCore has stopped any loading in this window and has removed any cross-scripting ability in javascript.
@ -396,8 +390,7 @@ class InAppBrowser {
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsAlert(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20android.webkit.JsResult) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsAlert(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20android.webkit.JsResult)
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1537406-webview ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1537406-webview
// ignore: missing_return Future<JsAlertResponse?>? onJsAlert(JsAlertRequest jsAlertRequest) {}
Future<JsAlertResponse> onJsAlert(JsAlertRequest jsAlertRequest) {}
///Event fired when javascript calls the `confirm()` method to display a confirm dialog. ///Event fired when javascript calls the `confirm()` method to display a confirm dialog.
///If [JsConfirmResponse.handledByClient] is `true`, the webview will assume that the client will handle the dialog. ///If [JsConfirmResponse.handledByClient] is `true`, the webview will assume that the client will handle the dialog.
@ -407,8 +400,7 @@ class InAppBrowser {
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsConfirm(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20android.webkit.JsResult) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsConfirm(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20android.webkit.JsResult)
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1536489-webview ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1536489-webview
// ignore: missing_return Future<JsConfirmResponse?>? onJsConfirm(JsConfirmRequest jsConfirmRequest) {}
Future<JsConfirmResponse> onJsConfirm(JsConfirmRequest jsConfirmRequest) {}
///Event fired when javascript calls the `prompt()` method to display a prompt dialog. ///Event fired when javascript calls the `prompt()` method to display a prompt dialog.
///If [JsPromptResponse.handledByClient] is `true`, the webview will assume that the client will handle the dialog. ///If [JsPromptResponse.handledByClient] is `true`, the webview will assume that the client will handle the dialog.
@ -418,8 +410,7 @@ class InAppBrowser {
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsPrompt(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20java.lang.String,%20android.webkit.JsPromptResult) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsPrompt(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20java.lang.String,%20android.webkit.JsPromptResult)
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1538086-webview ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1538086-webview
// ignore: missing_return Future<JsPromptResponse?>? onJsPrompt(JsPromptRequest jsPromptRequest) {}
Future<JsPromptResponse> onJsPrompt(JsPromptRequest jsPromptRequest) {}
///Event fired when the WebView received an HTTP authentication request. The default behavior is to cancel the request. ///Event fired when the WebView received an HTTP authentication request. The default behavior is to cancel the request.
/// ///
@ -428,8 +419,7 @@ class InAppBrowser {
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedHttpAuthRequest(android.webkit.WebView,%20android.webkit.HttpAuthHandler,%20java.lang.String,%20java.lang.String) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedHttpAuthRequest(android.webkit.WebView,%20android.webkit.HttpAuthHandler,%20java.lang.String,%20java.lang.String)
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview
// ignore: missing_return Future<HttpAuthResponse?>? onReceivedHttpAuthRequest(
Future<HttpAuthResponse> onReceivedHttpAuthRequest(
HttpAuthChallenge challenge) {} HttpAuthChallenge challenge) {}
///Event fired when the WebView need to perform server trust authentication (certificate validation). ///Event fired when the WebView need to perform server trust authentication (certificate validation).
@ -440,8 +430,7 @@ class InAppBrowser {
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedSslError(android.webkit.WebView,%20android.webkit.SslErrorHandler,%20android.net.http.SslError) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedSslError(android.webkit.WebView,%20android.webkit.SslErrorHandler,%20android.net.http.SslError)
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview
// ignore: missing_return Future<ServerTrustAuthResponse?>? onReceivedServerTrustAuthRequest(
Future<ServerTrustAuthResponse> onReceivedServerTrustAuthRequest(
ServerTrustChallenge challenge) {} ServerTrustChallenge challenge) {}
///Notify the host application to handle an SSL client certificate request. ///Notify the host application to handle an SSL client certificate request.
@ -454,8 +443,7 @@ class InAppBrowser {
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedClientCertRequest(android.webkit.WebView,%20android.webkit.ClientCertRequest) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedClientCertRequest(android.webkit.WebView,%20android.webkit.ClientCertRequest)
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview
// ignore: missing_return Future<ClientCertResponse?>? onReceivedClientCertRequest(
Future<ClientCertResponse> onReceivedClientCertRequest(
ClientCertChallenge challenge) {} ClientCertChallenge challenge) {}
///Event fired as find-on-page operations progress. ///Event fired as find-on-page operations progress.
@ -477,8 +465,7 @@ class InAppBrowser {
///[ajaxRequest] represents the `XMLHttpRequest`. ///[ajaxRequest] represents the `XMLHttpRequest`.
/// ///
///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldInterceptAjaxRequest] option to `true`. ///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldInterceptAjaxRequest] option to `true`.
// ignore: missing_return Future<AjaxRequest?>? shouldInterceptAjaxRequest(AjaxRequest ajaxRequest) {}
Future<AjaxRequest> shouldInterceptAjaxRequest(AjaxRequest ajaxRequest) {}
///Event fired whenever the `readyState` attribute of an `XMLHttpRequest` changes. ///Event fired whenever the `readyState` attribute of an `XMLHttpRequest` changes.
///It gives the host application a chance to abort the request. ///It gives the host application a chance to abort the request.
@ -486,8 +473,7 @@ class InAppBrowser {
///[ajaxRequest] represents the [XMLHttpRequest]. ///[ajaxRequest] represents the [XMLHttpRequest].
/// ///
///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldInterceptAjaxRequest] option to `true`. ///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldInterceptAjaxRequest] option to `true`.
// ignore: missing_return Future<AjaxRequestAction?>? onAjaxReadyStateChange(AjaxRequest ajaxRequest) {}
Future<AjaxRequestAction> onAjaxReadyStateChange(AjaxRequest ajaxRequest) {}
///Event fired as an `XMLHttpRequest` progress. ///Event fired as an `XMLHttpRequest` progress.
///It gives the host application a chance to abort the request. ///It gives the host application a chance to abort the request.
@ -495,8 +481,7 @@ class InAppBrowser {
///[ajaxRequest] represents the [XMLHttpRequest]. ///[ajaxRequest] represents the [XMLHttpRequest].
/// ///
///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldInterceptAjaxRequest] option to `true`. ///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldInterceptAjaxRequest] option to `true`.
// ignore: missing_return Future<AjaxRequestAction?>? onAjaxProgress(AjaxRequest ajaxRequest) {}
Future<AjaxRequestAction> onAjaxProgress(AjaxRequest ajaxRequest) {}
///Event fired when a request is sent to a server through [Fetch API](https://developer.mozilla.org/it/docs/Web/API/Fetch_API). ///Event fired when a request is sent to a server through [Fetch API](https://developer.mozilla.org/it/docs/Web/API/Fetch_API).
///It gives the host application a chance to take control over the request before sending it. ///It gives the host application a chance to take control over the request before sending it.
@ -504,8 +489,7 @@ class InAppBrowser {
///[fetchRequest] represents a resource request. ///[fetchRequest] represents a resource request.
/// ///
///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldInterceptFetchRequest] option to `true`. ///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldInterceptFetchRequest] option to `true`.
// ignore: missing_return Future<FetchRequest?>? shouldInterceptFetchRequest(FetchRequest fetchRequest) {}
Future<FetchRequest> shouldInterceptFetchRequest(FetchRequest fetchRequest) {}
///Event fired when the host application updates its visited links database. ///Event fired when the host application updates its visited links database.
///This event is also fired when the navigation state of the [InAppWebView] changes through the usage of ///This event is also fired when the navigation state of the [InAppWebView] changes through the usage of
@ -517,14 +501,14 @@ class InAppBrowser {
///[androidIsReload] indicates if this url is being reloaded. Available only on Android. ///[androidIsReload] indicates if this url is being reloaded. Available only on Android.
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#doUpdateVisitedHistory(android.webkit.WebView,%20java.lang.String,%20boolean) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#doUpdateVisitedHistory(android.webkit.WebView,%20java.lang.String,%20boolean)
void onUpdateVisitedHistory(String url, bool androidIsReload) {} void onUpdateVisitedHistory(String? url, bool? androidIsReload) {}
///Event fired when `window.print()` is called from JavaScript side. ///Event fired when `window.print()` is called from JavaScript side.
/// ///
///[url] represents the url on which is called. ///[url] represents the url on which is called.
/// ///
///**NOTE**: available on Android 21+. ///**NOTE**: available on Android 21+.
void onPrint(String url) {} void onPrint(String? url) {}
///Event fired when an HTML element of the webview has been clicked and held. ///Event fired when an HTML element of the webview has been clicked and held.
/// ///
@ -559,14 +543,14 @@ class InAppBrowser {
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onPageCommitVisible(android.webkit.WebView,%20java.lang.String) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onPageCommitVisible(android.webkit.WebView,%20java.lang.String)
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455635-webview ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455635-webview
void onPageCommitVisible(String url) {} void onPageCommitVisible(String? url) {}
///Event fired when a change in the document title occurred. ///Event fired when a change in the document title occurred.
/// ///
///[title] represents the string containing the new title of the document. ///[title] represents the string containing the new title of the document.
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onReceivedTitle(android.webkit.WebView,%20java.lang.String) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onReceivedTitle(android.webkit.WebView,%20java.lang.String)
void onTitleChanged(String title) {} void onTitleChanged(String? title) {}
///Event fired when the WebView notifies that a loading URL has been flagged by Safe Browsing. ///Event fired when the WebView notifies that a loading URL has been flagged by Safe Browsing.
///The default behavior is to show an interstitial to the user, with the reporting checkbox visible. ///The default behavior is to show an interstitial to the user, with the reporting checkbox visible.
@ -578,9 +562,8 @@ class InAppBrowser {
///**NOTE**: available only on Android 27+. ///**NOTE**: available only on Android 27+.
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onSafeBrowsingHit(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20int,%20android.webkit.SafeBrowsingResponse) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onSafeBrowsingHit(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20int,%20android.webkit.SafeBrowsingResponse)
// ignore: missing_return Future<SafeBrowsingResponse?>? androidOnSafeBrowsingHit(
Future<SafeBrowsingResponse> androidOnSafeBrowsingHit( String url, SafeBrowsingThreat? threatType) {}
String url, SafeBrowsingThreat threatType) {}
///Event fired when the WebView is requesting permission to access the specified resources and the permission currently isn't granted or denied. ///Event fired when the WebView is requesting permission to access the specified resources and the permission currently isn't granted or denied.
/// ///
@ -591,8 +574,7 @@ class InAppBrowser {
///**NOTE**: available only on Android 23+. ///**NOTE**: available only on Android 23+.
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onPermissionRequest(android.webkit.PermissionRequest) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onPermissionRequest(android.webkit.PermissionRequest)
// ignore: missing_return Future<PermissionRequestResponse?>? androidOnPermissionRequest(
Future<PermissionRequestResponse> androidOnPermissionRequest(
String origin, List<String> resources) {} String origin, List<String> resources) {}
///Event that notifies the host application that web content from the specified origin is attempting to use the Geolocation API, but no permission state is currently set for that origin. ///Event that notifies the host application that web content from the specified origin is attempting to use the Geolocation API, but no permission state is currently set for that origin.
@ -604,8 +586,7 @@ class InAppBrowser {
///**NOTE**: available only on Android. ///**NOTE**: available only on Android.
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onGeolocationPermissionsShowPrompt(java.lang.String,%20android.webkit.GeolocationPermissions.Callback) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onGeolocationPermissionsShowPrompt(java.lang.String,%20android.webkit.GeolocationPermissions.Callback)
Future<GeolocationPermissionShowPromptResponse> Future<GeolocationPermissionShowPromptResponse?>?
// ignore: missing_return
androidOnGeolocationPermissionsShowPrompt(String origin) {} androidOnGeolocationPermissionsShowPrompt(String origin) {}
///Notify the host application that a request for Geolocation permissions, made with a previous call to [androidOnGeolocationPermissionsShowPrompt] has been canceled. ///Notify the host application that a request for Geolocation permissions, made with a previous call to [androidOnGeolocationPermissionsShowPrompt] has been canceled.
@ -633,8 +614,7 @@ class InAppBrowser {
///**Official Android API**: ///**Official Android API**:
///- https://developer.android.com/reference/android/webkit/WebViewClient#shouldInterceptRequest(android.webkit.WebView,%20android.webkit.WebResourceRequest) ///- https://developer.android.com/reference/android/webkit/WebViewClient#shouldInterceptRequest(android.webkit.WebView,%20android.webkit.WebResourceRequest)
///- https://developer.android.com/reference/android/webkit/WebViewClient#shouldInterceptRequest(android.webkit.WebView,%20java.lang.String) ///- https://developer.android.com/reference/android/webkit/WebViewClient#shouldInterceptRequest(android.webkit.WebView,%20java.lang.String)
Future<WebResourceResponse> Future<WebResourceResponse?>?
// ignore: missing_return
androidShouldInterceptRequest(WebResourceRequest request) {} androidShouldInterceptRequest(WebResourceRequest request) {}
///Event called when the renderer currently associated with the WebView becomes unresponsive as a result of a long running blocking task such as the execution of JavaScript. ///Event called when the renderer currently associated with the WebView becomes unresponsive as a result of a long running blocking task such as the execution of JavaScript.
@ -655,9 +635,8 @@ class InAppBrowser {
///**NOTE**: available only on Android 29+. ///**NOTE**: available only on Android 29+.
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewRenderProcessClient#onRenderProcessUnresponsive(android.webkit.WebView,%20android.webkit.WebViewRenderProcess) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewRenderProcessClient#onRenderProcessUnresponsive(android.webkit.WebView,%20android.webkit.WebViewRenderProcess)
Future<WebViewRenderProcessAction> Future<WebViewRenderProcessAction?>?
// ignore: missing_return androidOnRenderProcessUnresponsive(String? url) {}
androidOnRenderProcessUnresponsive(String url) {}
///Event called once when an unresponsive renderer currently associated with the WebView becomes responsive. ///Event called once when an unresponsive renderer currently associated with the WebView becomes responsive.
/// ///
@ -670,9 +649,8 @@ class InAppBrowser {
///**NOTE**: available only on Android 29+. ///**NOTE**: available only on Android 29+.
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewRenderProcessClient#onRenderProcessResponsive(android.webkit.WebView,%20android.webkit.WebViewRenderProcess) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewRenderProcessClient#onRenderProcessResponsive(android.webkit.WebView,%20android.webkit.WebViewRenderProcess)
Future<WebViewRenderProcessAction> Future<WebViewRenderProcessAction?>?
// ignore: missing_return androidOnRenderProcessResponsive(String? url) {}
androidOnRenderProcessResponsive(String url) {}
///Event fired when the given WebView's render process has exited. ///Event fired when the given WebView's render process has exited.
///The application's implementation of this callback should only attempt to clean up the WebView. ///The application's implementation of this callback should only attempt to clean up the WebView.
@ -690,9 +668,8 @@ class InAppBrowser {
///**NOTE**: available only on Android. ///**NOTE**: available only on Android.
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onFormResubmission(android.webkit.WebView,%20android.os.Message,%20android.os.Message) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onFormResubmission(android.webkit.WebView,%20android.os.Message,%20android.os.Message)
Future<FormResubmissionAction> Future<FormResubmissionAction?>?
// ignore: missing_return androidOnFormResubmission(String? url) {}
androidOnFormResubmission(String url) {}
///Event fired when the scale applied to the WebView has changed. ///Event fired when the scale applied to the WebView has changed.
/// ///
@ -746,8 +723,7 @@ class InAppBrowser {
///**NOTE**: available only on Android. ///**NOTE**: available only on Android.
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsBeforeUnload(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20android.webkit.JsResult) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsBeforeUnload(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20android.webkit.JsResult)
// ignore: missing_return Future<JsBeforeUnloadResponse?>? androidOnJsBeforeUnload(
Future<JsBeforeUnloadResponse> androidOnJsBeforeUnload(
JsBeforeUnloadRequest jsBeforeUnloadRequest) {} JsBeforeUnloadRequest jsBeforeUnloadRequest) {}
///Event fired when a request to automatically log in the user has been processed. ///Event fired when a request to automatically log in the user has been processed.

View File

@ -6,7 +6,7 @@ import 'package:mime/mime.dart';
///This class allows you to create a simple server on `http://localhost:[port]/` in order to be able to load your assets file on a server. The default [port] value is `8080`. ///This class allows you to create a simple server on `http://localhost:[port]/` in order to be able to load your assets file on a server. The default [port] value is `8080`.
class InAppLocalhostServer { class InAppLocalhostServer {
HttpServer _server; HttpServer? _server;
int _port = 8080; int _port = 8080;
InAppLocalhostServer({int port = 8080}) { InAppLocalhostServer({int port = 8080}) {
@ -38,7 +38,7 @@ class InAppLocalhostServer {
this._server = server; this._server = server;
server.listen((HttpRequest request) async { server.listen((HttpRequest request) async {
var body = List<int>(); var body = [] as List<int>;
var path = request.requestedUri.path; var path = request.requestedUri.path;
path = (path.startsWith('/')) ? path.substring(1) : path; path = (path.startsWith('/')) ? path.substring(1) : path;
path += (path.endsWith('/')) ? 'index.html' : ''; path += (path.endsWith('/')) ? 'index.html' : '';
@ -77,7 +77,7 @@ class InAppLocalhostServer {
///Closes the server. ///Closes the server.
Future<void> close() async { Future<void> close() async {
if (this._server != null) { if (this._server != null) {
await this._server.close(force: true); await this._server!.close(force: true);
print('Server running on http://localhost:$_port closed'); print('Server running on http://localhost:$_port closed');
this._server = null; this._server = null;
} }

View File

@ -22,13 +22,13 @@ class InAppWebView extends StatefulWidget implements WebView {
/// recognizers on this list. /// recognizers on this list.
/// When `gestureRecognizers` is empty or null, the web view will only handle pointer events for gestures that /// When `gestureRecognizers` is empty or null, the web view will only handle pointer events for gestures that
/// were not claimed by any other gesture recognizer. /// were not claimed by any other gesture recognizer.
final Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers; final Set<Factory<OneSequenceGestureRecognizer>>? gestureRecognizers;
///The window id of a [CreateWindowRequest.windowId]. ///The window id of a [CreateWindowRequest.windowId].
final int windowId; final int? windowId;
const InAppWebView({ const InAppWebView({
Key key, Key? key,
this.windowId, this.windowId,
this.initialUrl = "about:blank", this.initialUrl = "about:blank",
this.initialFile, this.initialFile,
@ -94,248 +94,248 @@ class InAppWebView extends StatefulWidget implements WebView {
_InAppWebViewState createState() => _InAppWebViewState(); _InAppWebViewState createState() => _InAppWebViewState();
@override @override
final void Function(InAppWebViewController controller) final void Function(InAppWebViewController controller)?
androidOnGeolocationPermissionsHidePrompt; androidOnGeolocationPermissionsHidePrompt;
@override @override
final Future<GeolocationPermissionShowPromptResponse> Function( final Future<GeolocationPermissionShowPromptResponse?> Function(
InAppWebViewController controller, String origin) InAppWebViewController controller, String origin)?
androidOnGeolocationPermissionsShowPrompt; androidOnGeolocationPermissionsShowPrompt;
@override @override
final Future<PermissionRequestResponse> Function( final Future<PermissionRequestResponse?> Function(
InAppWebViewController controller, InAppWebViewController controller,
String origin, String origin,
List<String> resources) androidOnPermissionRequest; List<String> resources)? androidOnPermissionRequest;
@override @override
final Future<SafeBrowsingResponse> Function(InAppWebViewController controller, final Future<SafeBrowsingResponse?> Function(InAppWebViewController controller,
String url, SafeBrowsingThreat threatType) androidOnSafeBrowsingHit; String url, SafeBrowsingThreat? threatType)? androidOnSafeBrowsingHit;
@override @override
final InAppWebViewInitialData initialData; final InAppWebViewInitialData? initialData;
@override @override
final String initialFile; final String? initialFile;
@override @override
final Map<String, String> initialHeaders; final Map<String, String>? initialHeaders;
@override @override
final InAppWebViewGroupOptions initialOptions; final InAppWebViewGroupOptions? initialOptions;
@override @override
final String initialUrl; final String? initialUrl;
@override @override
final ContextMenu contextMenu; final ContextMenu? contextMenu;
@override @override
final void Function(InAppWebViewController controller, String url) final void Function(InAppWebViewController controller, String? url)?
onPageCommitVisible; onPageCommitVisible;
@override @override
final void Function(InAppWebViewController controller, String title) final void Function(InAppWebViewController controller, String? title)?
onTitleChanged; onTitleChanged;
@override @override
final void Function(InAppWebViewController controller) final void Function(InAppWebViewController controller)?
iosOnDidReceiveServerRedirectForProvisionalNavigation; iosOnDidReceiveServerRedirectForProvisionalNavigation;
@override @override
final void Function(InAppWebViewController controller) final void Function(InAppWebViewController controller)?
iosOnWebContentProcessDidTerminate; iosOnWebContentProcessDidTerminate;
@override @override
final Future<AjaxRequestAction> Function( final Future<AjaxRequestAction> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest) InAppWebViewController controller, AjaxRequest ajaxRequest)?
onAjaxProgress; onAjaxProgress;
@override @override
final Future<AjaxRequestAction> Function( final Future<AjaxRequestAction?> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest) InAppWebViewController controller, AjaxRequest ajaxRequest)?
onAjaxReadyStateChange; onAjaxReadyStateChange;
@override @override
final void Function( final void Function(
InAppWebViewController controller, ConsoleMessage consoleMessage) InAppWebViewController controller, ConsoleMessage consoleMessage)?
onConsoleMessage; onConsoleMessage;
@override @override
final Future<bool> Function(InAppWebViewController controller, final Future<bool?> Function(InAppWebViewController controller,
CreateWindowRequest createWindowRequest) onCreateWindow; CreateWindowRequest createWindowRequest)? onCreateWindow;
@override @override
final void Function(InAppWebViewController controller) onCloseWindow; final void Function(InAppWebViewController controller)? onCloseWindow;
@override @override
final void Function(InAppWebViewController controller) onWindowFocus; final void Function(InAppWebViewController controller)? onWindowFocus;
@override @override
final void Function(InAppWebViewController controller) onWindowBlur; final void Function(InAppWebViewController controller)? onWindowBlur;
@override @override
final void Function(InAppWebViewController controller) androidOnRequestFocus; final void Function(InAppWebViewController controller)? androidOnRequestFocus;
@override @override
final void Function(InAppWebViewController controller, Uint8List icon) final void Function(InAppWebViewController controller, Uint8List icon)?
androidOnReceivedIcon; androidOnReceivedIcon;
@override @override
final void Function( final void Function(
InAppWebViewController controller, String url, bool precomposed) InAppWebViewController controller, String url, bool precomposed)?
androidOnReceivedTouchIconUrl; androidOnReceivedTouchIconUrl;
@override @override
final void Function(InAppWebViewController controller, String url) final void Function(InAppWebViewController controller, String url)?
onDownloadStart; onDownloadStart;
@override @override
final void Function(InAppWebViewController controller, int activeMatchOrdinal, final void Function(InAppWebViewController controller, int activeMatchOrdinal,
int numberOfMatches, bool isDoneCounting) onFindResultReceived; int numberOfMatches, bool isDoneCounting)? onFindResultReceived;
@override @override
final Future<JsAlertResponse> Function( final Future<JsAlertResponse?> Function(
InAppWebViewController controller, JsAlertRequest jsAlertRequest) InAppWebViewController controller, JsAlertRequest jsAlertRequest)?
onJsAlert; onJsAlert;
@override @override
final Future<JsConfirmResponse> Function( final Future<JsConfirmResponse?> Function(
InAppWebViewController controller, JsConfirmRequest jsConfirmRequest) InAppWebViewController controller, JsConfirmRequest jsConfirmRequest)?
onJsConfirm; onJsConfirm;
@override @override
final Future<JsPromptResponse> Function( final Future<JsPromptResponse?> Function(
InAppWebViewController controller, JsPromptRequest jsPromptRequest) InAppWebViewController controller, JsPromptRequest jsPromptRequest)?
onJsPrompt; onJsPrompt;
@override @override
final void Function(InAppWebViewController controller, String url, int code, final void Function(InAppWebViewController controller, String? url, int code,
String message) onLoadError; String message)? onLoadError;
@override @override
final void Function(InAppWebViewController controller, String url, final void Function(InAppWebViewController controller, String? url,
int statusCode, String description) onLoadHttpError; int statusCode, String description)? onLoadHttpError;
@override @override
final void Function( final void Function(
InAppWebViewController controller, LoadedResource resource) InAppWebViewController controller, LoadedResource resource)?
onLoadResource; onLoadResource;
@override @override
final Future<CustomSchemeResponse> Function( final Future<CustomSchemeResponse?> Function(
InAppWebViewController controller, String scheme, String url) InAppWebViewController controller, String scheme, String url)?
onLoadResourceCustomScheme; onLoadResourceCustomScheme;
@override @override
final void Function(InAppWebViewController controller, String url) final void Function(InAppWebViewController controller, String? url)?
onLoadStart; onLoadStart;
@override @override
final void Function(InAppWebViewController controller, String url) onLoadStop; final void Function(InAppWebViewController controller, String? url)? onLoadStop;
@override @override
final void Function(InAppWebViewController controller, final void Function(InAppWebViewController controller,
InAppWebViewHitTestResult hitTestResult) onLongPressHitTestResult; InAppWebViewHitTestResult hitTestResult)? onLongPressHitTestResult;
@override @override
final void Function(InAppWebViewController controller, String url) onPrint; final void Function(InAppWebViewController controller, String? url)? onPrint;
@override @override
final void Function(InAppWebViewController controller, int progress) final void Function(InAppWebViewController controller, int progress)?
onProgressChanged; onProgressChanged;
@override @override
final Future<ClientCertResponse> Function( final Future<ClientCertResponse?> Function(
InAppWebViewController controller, ClientCertChallenge challenge) InAppWebViewController controller, ClientCertChallenge challenge)?
onReceivedClientCertRequest; onReceivedClientCertRequest;
@override @override
final Future<HttpAuthResponse> Function( final Future<HttpAuthResponse?> Function(
InAppWebViewController controller, HttpAuthChallenge challenge) InAppWebViewController controller, HttpAuthChallenge challenge)?
onReceivedHttpAuthRequest; onReceivedHttpAuthRequest;
@override @override
final Future<ServerTrustAuthResponse> Function( final Future<ServerTrustAuthResponse?> Function(
InAppWebViewController controller, ServerTrustChallenge challenge) InAppWebViewController controller, ServerTrustChallenge challenge)?
onReceivedServerTrustAuthRequest; onReceivedServerTrustAuthRequest;
@override @override
final void Function(InAppWebViewController controller, int x, int y) final void Function(InAppWebViewController controller, int x, int y)?
onScrollChanged; onScrollChanged;
@override @override
final void Function( final void Function(
InAppWebViewController controller, String url, bool androidIsReload) InAppWebViewController controller, String? url, bool? androidIsReload)?
onUpdateVisitedHistory; onUpdateVisitedHistory;
@override @override
final void Function(InAppWebViewController controller) onWebViewCreated; final void Function(InAppWebViewController controller)? onWebViewCreated;
@override @override
final Future<AjaxRequest> Function( final Future<AjaxRequest?> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest) InAppWebViewController controller, AjaxRequest ajaxRequest)?
shouldInterceptAjaxRequest; shouldInterceptAjaxRequest;
@override @override
final Future<FetchRequest> Function( final Future<FetchRequest?> Function(
InAppWebViewController controller, FetchRequest fetchRequest) InAppWebViewController controller, FetchRequest fetchRequest)?
shouldInterceptFetchRequest; shouldInterceptFetchRequest;
@override @override
final Future<ShouldOverrideUrlLoadingAction> Function( final Future<ShouldOverrideUrlLoadingAction?> Function(
InAppWebViewController controller, InAppWebViewController controller,
ShouldOverrideUrlLoadingRequest shouldOverrideUrlLoadingRequest) ShouldOverrideUrlLoadingRequest shouldOverrideUrlLoadingRequest)?
shouldOverrideUrlLoading; shouldOverrideUrlLoading;
@override @override
final void Function(InAppWebViewController controller) onEnterFullscreen; final void Function(InAppWebViewController controller)? onEnterFullscreen;
@override @override
final void Function(InAppWebViewController controller) onExitFullscreen; final void Function(InAppWebViewController controller)? onExitFullscreen;
@override @override
final Future<WebResourceResponse> Function( final Future<WebResourceResponse?> Function(
InAppWebViewController controller, WebResourceRequest request) InAppWebViewController controller, WebResourceRequest request)?
androidShouldInterceptRequest; androidShouldInterceptRequest;
@override @override
final Future<WebViewRenderProcessAction> Function( final Future<WebViewRenderProcessAction?> Function(
InAppWebViewController controller, String url) InAppWebViewController controller, String? url)?
androidOnRenderProcessUnresponsive; androidOnRenderProcessUnresponsive;
@override @override
final Future<WebViewRenderProcessAction> Function( final Future<WebViewRenderProcessAction?> Function(
InAppWebViewController controller, String url) InAppWebViewController controller, String? url)?
androidOnRenderProcessResponsive; androidOnRenderProcessResponsive;
@override @override
final void Function( final void Function(
InAppWebViewController controller, RenderProcessGoneDetail detail) InAppWebViewController controller, RenderProcessGoneDetail detail)?
androidOnRenderProcessGone; androidOnRenderProcessGone;
@override @override
final Future<FormResubmissionAction> Function( final Future<FormResubmissionAction?> Function(
InAppWebViewController controller, String url) androidOnFormResubmission; InAppWebViewController controller, String? url)? androidOnFormResubmission;
@override @override
final void Function( final void Function(
InAppWebViewController controller, double oldScale, double newScale) InAppWebViewController controller, double oldScale, double newScale)?
androidOnScaleChanged; androidOnScaleChanged;
@override @override
final Future<JsBeforeUnloadResponse> Function( final Future<JsBeforeUnloadResponse?> Function(
InAppWebViewController controller, InAppWebViewController controller,
JsBeforeUnloadRequest jsBeforeUnloadRequest) androidOnJsBeforeUnload; JsBeforeUnloadRequest jsBeforeUnloadRequest)? androidOnJsBeforeUnload;
@override @override
final void Function( final void Function(
InAppWebViewController controller, LoginRequest loginRequest) InAppWebViewController controller, LoginRequest loginRequest)?
androidOnReceivedLoginRequest; androidOnReceivedLoginRequest;
} }
class _InAppWebViewState extends State<InAppWebView> { class _InAppWebViewState extends State<InAppWebView> {
InAppWebViewController _controller; late InAppWebViewController _controller;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -348,7 +348,7 @@ class _InAppWebViewState extends State<InAppWebView> {
PlatformViewController controller, PlatformViewController controller,
) { ) {
return AndroidViewSurface( return AndroidViewSurface(
controller: controller, controller: controller as AndroidViewController,
gestureRecognizers: widget.gestureRecognizers ?? const <Factory<OneSequenceGestureRecognizer>>{}, gestureRecognizers: widget.gestureRecognizers ?? const <Factory<OneSequenceGestureRecognizer>>{},
hitTestBehavior: PlatformViewHitTestBehavior.opaque, hitTestBehavior: PlatformViewHitTestBehavior.opaque,
); );
@ -359,7 +359,7 @@ class _InAppWebViewState extends State<InAppWebView> {
viewType: 'com.pichillilorenzo/flutter_inappwebview', viewType: 'com.pichillilorenzo/flutter_inappwebview',
layoutDirection: TextDirection.rtl, layoutDirection: TextDirection.rtl,
creationParams: <String, dynamic>{ creationParams: <String, dynamic>{
'initialUrl': '${Uri.parse(widget.initialUrl)}', 'initialUrl': widget.initialUrl != null ? '${Uri.parse(widget.initialUrl!)}' : '',
'initialFile': widget.initialFile, 'initialFile': widget.initialFile,
'initialData': widget.initialData?.toMap(), 'initialData': widget.initialData?.toMap(),
'initialHeaders': widget.initialHeaders, 'initialHeaders': widget.initialHeaders,
@ -381,7 +381,7 @@ class _InAppWebViewState extends State<InAppWebView> {
gestureRecognizers: widget.gestureRecognizers, gestureRecognizers: widget.gestureRecognizers,
layoutDirection: TextDirection.rtl, layoutDirection: TextDirection.rtl,
creationParams: <String, dynamic>{ creationParams: <String, dynamic>{
'initialUrl': '${Uri.parse(widget.initialUrl)}', 'initialUrl': widget.initialUrl != null ? '${Uri.parse(widget.initialUrl!)}' : '',
'initialFile': widget.initialFile, 'initialFile': widget.initialFile,
'initialData': widget.initialData?.toMap(), 'initialData': widget.initialData?.toMap(),
'initialHeaders': widget.initialHeaders, 'initialHeaders': widget.initialHeaders,
@ -398,7 +398,7 @@ class _InAppWebViewState extends State<InAppWebView> {
onPlatformViewCreated: _onPlatformViewCreated, onPlatformViewCreated: _onPlatformViewCreated,
gestureRecognizers: widget.gestureRecognizers, gestureRecognizers: widget.gestureRecognizers,
creationParams: <String, dynamic>{ creationParams: <String, dynamic>{
'initialUrl': '${Uri.parse(widget.initialUrl)}', 'initialUrl': widget.initialUrl != null ? '${Uri.parse(widget.initialUrl!)}' : '',
'initialFile': widget.initialFile, 'initialFile': widget.initialFile,
'initialData': widget.initialData?.toMap(), 'initialData': widget.initialData?.toMap(),
'initialHeaders': widget.initialHeaders, 'initialHeaders': widget.initialHeaders,
@ -426,7 +426,7 @@ class _InAppWebViewState extends State<InAppWebView> {
void _onPlatformViewCreated(int id) { void _onPlatformViewCreated(int id) {
_controller = InAppWebViewController(id, widget); _controller = InAppWebViewController(id, widget);
if (widget.onWebViewCreated != null) { if (widget.onWebViewCreated != null) {
widget.onWebViewCreated(_controller); widget.onWebViewCreated!(_controller);
} }
} }
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@ import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class Util { class Util {
static Color convertColorFromStringRepresentation(String colorValue) { static Color? convertColorFromStringRepresentation(String colorValue) {
if (colorValue.startsWith("#")) { if (colorValue.startsWith("#")) {
return Util.getColorFromHex(colorValue); return Util.getColorFromHex(colorValue);
} else if (colorValue.startsWith("rgb(")) { } else if (colorValue.startsWith("rgb(")) {
@ -391,7 +391,7 @@ class Util {
rgbaValues[0], rgbaValues[1], rgbaValues[2], hlsaValues[3]); rgbaValues[0], rgbaValues[1], rgbaValues[2], hlsaValues[3]);
} }
static List<num> hslToRgb(double h, double s, double l) { static List<int> hslToRgb(double h, double s, double l) {
double r, g, b; double r, g, b;
if (s == 0) { if (s == 0) {
@ -407,8 +407,8 @@ class Util {
return rgb; return rgb;
} }
static num to255(double v) { static int to255(double v) {
return min(255, 256 * v); return min(255, (256 * v).round());
} }
/// Helper method that converts hue to rgb /// Helper method that converts hue to rgb

View File

@ -1,7 +1,5 @@
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'in_app_webview_controller.dart'; import 'in_app_webview_controller.dart';
import 'types.dart'; import 'types.dart';
@ -14,13 +12,13 @@ class WebStorage {
///Represents `window.sessionStorage`. ///Represents `window.sessionStorage`.
SessionStorage sessionStorage; SessionStorage sessionStorage;
WebStorage({@required this.localStorage, @required this.sessionStorage}); WebStorage({required this.localStorage, required this.sessionStorage});
} }
///Class that represents a single web storage item of the JavaScript `window.sessionStorage` and `window.localStorage` objects. ///Class that represents a single web storage item of the JavaScript `window.sessionStorage` and `window.localStorage` objects.
class WebStorageItem { class WebStorageItem {
///Item key. ///Item key.
String key; String? key;
///Item value. ///Item value.
dynamic value; dynamic value;
@ -47,18 +45,17 @@ class WebStorageItem {
///Class that provides methods to manage the JavaScript [Storage](https://developer.mozilla.org/en-US/docs/Web/API/Storage) object. ///Class that provides methods to manage the JavaScript [Storage](https://developer.mozilla.org/en-US/docs/Web/API/Storage) object.
///It is used by [LocalStorage] and [SessionStorage]. ///It is used by [LocalStorage] and [SessionStorage].
class Storage { class Storage {
InAppWebViewController _controller; late InAppWebViewController _controller;
///The web storage type: `window.sessionStorage` or `window.localStorage`. ///The web storage type: `window.sessionStorage` or `window.localStorage`.
WebStorageType webStorageType; WebStorageType webStorageType;
Storage(InAppWebViewController controller, this.webStorageType) { Storage(InAppWebViewController controller, this.webStorageType) {
assert(controller != null && this.webStorageType != null);
this._controller = controller; this._controller = controller;
} }
///Returns an integer representing the number of data items stored in the Storage object. ///Returns an integer representing the number of data items stored in the Storage object.
Future<int> length() async { Future<int?> length() async {
var result = await _controller.evaluateJavascript(source: """ var result = await _controller.evaluateJavascript(source: """
window.$webStorageType.length; window.$webStorageType.length;
"""); """);
@ -66,7 +63,7 @@ class Storage {
} }
///When passed a [key] name and [value], will add that key to the storage, or update that key's value if it already exists. ///When passed a [key] name and [value], will add that key to the storage, or update that key's value if it already exists.
Future<void> setItem({@required String key, @required dynamic value}) async { Future<void> setItem({required String key, required dynamic value}) async {
var encodedValue = json.encode(value); var encodedValue = json.encode(value);
await _controller.evaluateJavascript(source: """ await _controller.evaluateJavascript(source: """
window.$webStorageType.setItem("$key", ${value is String ? encodedValue : "JSON.stringify($encodedValue)"}); window.$webStorageType.setItem("$key", ${value is String ? encodedValue : "JSON.stringify($encodedValue)"});
@ -74,7 +71,7 @@ class Storage {
} }
///When passed a [key] name, will return that key's value, or `null` if the key does not exist, in the given Storage object. ///When passed a [key] name, will return that key's value, or `null` if the key does not exist, in the given Storage object.
Future<dynamic> getItem({@required String key}) async { Future<dynamic> getItem({required String key}) async {
var itemValue = await _controller.evaluateJavascript(source: """ var itemValue = await _controller.evaluateJavascript(source: """
window.$webStorageType.getItem("$key"); window.$webStorageType.getItem("$key");
"""); """);
@ -91,7 +88,7 @@ class Storage {
} }
///When passed a [key] name, will remove that key from the given Storage object if it exists. ///When passed a [key] name, will remove that key from the given Storage object if it exists.
Future<void> removeItem({@required String key}) async { Future<void> removeItem({required String key}) async {
await _controller.evaluateJavascript(source: """ await _controller.evaluateJavascript(source: """
window.$webStorageType.removeItem("$key"); window.$webStorageType.removeItem("$key");
"""); """);
@ -139,7 +136,7 @@ class Storage {
///When passed a number [index], returns the name of the nth key in a given Storage object. ///When passed a number [index], returns the name of the nth key in a given Storage object.
///The order of keys is user-agent defined, so you should not rely on it. ///The order of keys is user-agent defined, so you should not rely on it.
Future<String> key({@required int index}) async { Future<String> key({required int index}) async {
var result = await _controller.evaluateJavascript(source: """ var result = await _controller.evaluateJavascript(source: """
window.$webStorageType.key($index); window.$webStorageType.key($index);
"""); """);

View File

@ -1,6 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'types.dart'; import 'types.dart';
@ -11,7 +10,7 @@ import 'types.dart';
/// ///
///**NOTE for iOS**: available from iOS 9.0+. ///**NOTE for iOS**: available from iOS 9.0+.
class WebStorageManager { class WebStorageManager {
static WebStorageManager _instance; static WebStorageManager? _instance;
static const MethodChannel _channel = const MethodChannel( static const MethodChannel _channel = const MethodChannel(
'com.pichillilorenzo/flutter_inappwebview_webstoragemanager'); 'com.pichillilorenzo/flutter_inappwebview_webstoragemanager');
@ -20,13 +19,13 @@ class WebStorageManager {
///Gets the WebStorage manager shared instance. ///Gets the WebStorage manager shared instance.
static WebStorageManager instance() { static WebStorageManager instance() {
return (_instance != null) ? _instance : _init(); return (_instance != null) ? _instance! : _init();
} }
static WebStorageManager _init() { static WebStorageManager _init() {
_channel.setMethodCallHandler(_handleMethod); _channel.setMethodCallHandler(_handleMethod);
_instance = new WebStorageManager(); _instance = new WebStorageManager();
return _instance; return _instance!;
} }
static Future<dynamic> _handleMethod(MethodCall call) async {} static Future<dynamic> _handleMethod(MethodCall call) async {}
@ -63,8 +62,7 @@ class AndroidWebStorageManager {
///Clears the storage currently being used by both the Application Cache and Web SQL Database APIs by the given [origin]. ///Clears the storage currently being used by both the Application Cache and Web SQL Database APIs by the given [origin].
///The origin is specified using its string representation. ///The origin is specified using its string representation.
Future<void> deleteOrigin({@required String origin}) async { Future<void> deleteOrigin({required String origin}) async {
assert(origin != null);
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("origin", () => origin); args.putIfAbsent("origin", () => origin);
await WebStorageManager._channel.invokeMethod('deleteOrigin', args); await WebStorageManager._channel.invokeMethod('deleteOrigin', args);
@ -73,8 +71,7 @@ class AndroidWebStorageManager {
///Gets the storage quota for the Web SQL Database API for the given [origin]. ///Gets the storage quota for the Web SQL Database API for the given [origin].
///The quota is given in bytes and the origin is specified using its string representation. ///The quota is given in bytes and the origin is specified using its string representation.
///Note that a quota is not enforced on a per-origin basis for the Application Cache API. ///Note that a quota is not enforced on a per-origin basis for the Application Cache API.
Future<int> getQuotaForOrigin({@required String origin}) async { Future<int> getQuotaForOrigin({required String origin}) async {
assert(origin != null);
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("origin", () => origin); args.putIfAbsent("origin", () => origin);
return await WebStorageManager._channel return await WebStorageManager._channel
@ -83,8 +80,7 @@ class AndroidWebStorageManager {
///Gets the amount of storage currently being used by both the Application Cache and Web SQL Database APIs by the given [origin]. ///Gets the amount of storage currently being used by both the Application Cache and Web SQL Database APIs by the given [origin].
///The amount is given in bytes and the origin is specified using its string representation. ///The amount is given in bytes and the origin is specified using its string representation.
Future<int> getUsageForOrigin({@required String origin}) async { Future<int> getUsageForOrigin({required String origin}) async {
assert(origin != null);
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("origin", () => origin); args.putIfAbsent("origin", () => origin);
return await WebStorageManager._channel return await WebStorageManager._channel
@ -101,8 +97,7 @@ class IOSWebStorageManager {
/// ///
///[dataTypes] represents the website data types to fetch records for. ///[dataTypes] represents the website data types to fetch records for.
Future<List<IOSWKWebsiteDataRecord>> fetchDataRecords( Future<List<IOSWKWebsiteDataRecord>> fetchDataRecords(
{@required Set<IOSWKWebsiteDataType> dataTypes}) async { {required Set<IOSWKWebsiteDataType> dataTypes}) async {
assert(dataTypes != null);
List<IOSWKWebsiteDataRecord> recordList = []; List<IOSWKWebsiteDataRecord> recordList = [];
List<String> dataTypesList = []; List<String> dataTypesList = [];
for (var dataType in dataTypes) { for (var dataType in dataTypes) {
@ -116,8 +111,11 @@ class IOSWebStorageManager {
for (var record in records) { for (var record in records) {
List<String> dataTypesString = record["dataTypes"].cast<String>(); List<String> dataTypesString = record["dataTypes"].cast<String>();
Set<IOSWKWebsiteDataType> dataTypes = Set(); Set<IOSWKWebsiteDataType> dataTypes = Set();
for (var dataType in dataTypesString) { for (var dataTypeValue in dataTypesString) {
dataTypes.add(IOSWKWebsiteDataType.fromValue(dataType)); var dataType = IOSWKWebsiteDataType.fromValue(dataTypeValue);
if (dataType != null) {
dataTypes.add(dataType);
}
} }
recordList.add(IOSWKWebsiteDataRecord( recordList.add(IOSWKWebsiteDataRecord(
displayName: record["displayName"], dataTypes: dataTypes)); displayName: record["displayName"], dataTypes: dataTypes));
@ -131,10 +129,8 @@ class IOSWebStorageManager {
/// ///
///[dataRecords] represents the website data records to delete website data for. ///[dataRecords] represents the website data records to delete website data for.
Future<void> removeDataFor( Future<void> removeDataFor(
{@required Set<IOSWKWebsiteDataType> dataTypes, {required Set<IOSWKWebsiteDataType> dataTypes,
@required List<IOSWKWebsiteDataRecord> dataRecords}) async { required List<IOSWKWebsiteDataRecord> dataRecords}) async {
assert(dataTypes != null && dataRecords != null);
List<String> dataTypesList = []; List<String> dataTypesList = [];
for (var dataType in dataTypes) { for (var dataType in dataTypes) {
dataTypesList.add(dataType.toValue()); dataTypesList.add(dataType.toValue());
@ -157,10 +153,8 @@ class IOSWebStorageManager {
/// ///
///[date] represents a date. All website data modified after this date will be removed. ///[date] represents a date. All website data modified after this date will be removed.
Future<void> removeDataModifiedSince( Future<void> removeDataModifiedSince(
{@required Set<IOSWKWebsiteDataType> dataTypes, {required Set<IOSWKWebsiteDataType> dataTypes,
@required DateTime date}) async { required DateTime date}) async {
assert(dataTypes != null && date != null);
List<String> dataTypesList = []; List<String> dataTypesList = [];
for (var dataType in dataTypes) { for (var dataType in dataTypes) {
dataTypesList.add(dataType.toValue()); dataTypesList.add(dataType.toValue());

View File

@ -7,20 +7,20 @@ import 'in_app_webview_controller.dart';
import 'webview_options.dart'; import 'webview_options.dart';
import 'headless_in_app_webview.dart'; import 'headless_in_app_webview.dart';
///Abstract class that represents a WebView. Used by [WebView] and [HeadlessInAppWebView]. ///Abstract class that represents a WebView. Used by [InAppWebView] and [HeadlessInAppWebView].
abstract class WebView { abstract class WebView {
///The window id of a [CreateWindowRequest.windowId]. ///The window id of a [CreateWindowRequest.windowId].
final int windowId; final int? windowId;
///Event fired when the [WebView] is created. ///Event fired when the [WebView] is created.
final void Function(InAppWebViewController controller) onWebViewCreated; final void Function(InAppWebViewController controller)? onWebViewCreated;
///Event fired when the [WebView] starts to load an [url]. ///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 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 ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455621-webview
final void Function(InAppWebViewController controller, String url) final void Function(InAppWebViewController controller, String? url)?
onLoadStart; onLoadStart;
///Event fired when the [WebView] finishes loading an [url]. ///Event fired when the [WebView] finishes loading an [url].
@ -28,15 +28,15 @@ abstract class WebView {
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onPageFinished(android.webkit.WebView,%20java.lang.String) ///**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 ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455629-webview
final void Function(InAppWebViewController controller, String url) onLoadStop; final void Function(InAppWebViewController controller, String? url)? onLoadStop;
///Event fired when the [WebView] encounters an error loading an [url]. ///Event fired when the [WebView] encounters an error loading an [url].
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedError(android.webkit.WebView,%20int,%20java.lang.String,%20java.lang.String) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedError(android.webkit.WebView,%20int,%20java.lang.String,%20java.lang.String)
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455623-webview ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455623-webview
final void Function(InAppWebViewController controller, String url, int code, final void Function(InAppWebViewController controller, String? url, int code,
String message) onLoadError; String message)? onLoadError;
///Event fired when the [WebView] main page receives an HTTP error. ///Event fired when the [WebView] main page receives an HTTP error.
/// ///
@ -51,20 +51,20 @@ abstract class WebView {
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedHttpError(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20android.webkit.WebResourceResponse) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedHttpError(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20android.webkit.WebResourceResponse)
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview
final void Function(InAppWebViewController controller, String url, final void Function(InAppWebViewController controller, String? url,
int statusCode, String description) onLoadHttpError; int statusCode, String description)? onLoadHttpError;
///Event fired when the current [progress] of loading a page is changed. ///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) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onProgressChanged(android.webkit.WebView,%20int)
final void Function(InAppWebViewController controller, int progress) final void Function(InAppWebViewController controller, int progress)?
onProgressChanged; onProgressChanged;
///Event fired when the [WebView] receives a [ConsoleMessage]. ///Event fired when the [WebView] receives a [ConsoleMessage].
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onConsoleMessage(android.webkit.ConsoleMessage) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onConsoleMessage(android.webkit.ConsoleMessage)
final void Function( final void Function(
InAppWebViewController controller, ConsoleMessage consoleMessage) InAppWebViewController controller, ConsoleMessage consoleMessage)?
onConsoleMessage; onConsoleMessage;
///Give the host application a chance to take control when a URL is about to be loaded in the current WebView. This event is not called on the initial load of the WebView. ///Give the host application a chance to take control when a URL is about to be loaded in the current WebView. This event is not called on the initial load of the WebView.
@ -82,16 +82,16 @@ abstract class WebView {
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#shouldOverrideUrlLoading(android.webkit.WebView,%20java.lang.String) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#shouldOverrideUrlLoading(android.webkit.WebView,%20java.lang.String)
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455641-webview ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455641-webview
final Future<ShouldOverrideUrlLoadingAction> Function( final Future<ShouldOverrideUrlLoadingAction?> Function(
InAppWebViewController controller, InAppWebViewController controller,
ShouldOverrideUrlLoadingRequest shouldOverrideUrlLoadingRequest) ShouldOverrideUrlLoadingRequest shouldOverrideUrlLoadingRequest)?
shouldOverrideUrlLoading; shouldOverrideUrlLoading;
///Event fired when the [WebView] loads a resource. ///Event fired when the [WebView] loads a resource.
/// ///
///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useOnLoadResource] and [InAppWebViewOptions.javaScriptEnabled] options to `true`. ///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useOnLoadResource] and [InAppWebViewOptions.javaScriptEnabled] options to `true`.
final void Function( final void Function(
InAppWebViewController controller, LoadedResource resource) InAppWebViewController controller, LoadedResource resource)?
onLoadResource; onLoadResource;
///Event fired when the [WebView] scrolls. ///Event fired when the [WebView] scrolls.
@ -103,7 +103,7 @@ abstract class WebView {
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#onScrollChanged(int,%20int,%20int,%20int) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#onScrollChanged(int,%20int,%20int,%20int)
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiscrollviewdelegate/1619392-scrollviewdidscroll ///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiscrollviewdelegate/1619392-scrollviewdidscroll
final void Function(InAppWebViewController controller, int x, int y) final void Function(InAppWebViewController controller, int x, int y)?
onScrollChanged; onScrollChanged;
///Event fired when [WebView] recognizes a downloadable file. ///Event fired when [WebView] recognizes a downloadable file.
@ -116,7 +116,7 @@ abstract class WebView {
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#setDownloadListener(android.webkit.DownloadListener) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#setDownloadListener(android.webkit.DownloadListener)
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview
final void Function(InAppWebViewController controller, String url) final void Function(InAppWebViewController controller, String url)?
onDownloadStart; onDownloadStart;
///Event fired when the [WebView] finds the `custom-scheme` while loading a resource. Here you can handle the url request and return a [CustomSchemeResponse] to load a specific resource encoded to `base64`. ///Event fired when the [WebView] finds the `custom-scheme` while loading a resource. Here you can handle the url request and return a [CustomSchemeResponse] to load a specific resource encoded to `base64`.
@ -126,8 +126,8 @@ abstract class WebView {
///[url] represents the url of the request. ///[url] represents the url of the request.
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkurlschemehandler ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkurlschemehandler
final Future<CustomSchemeResponse> Function( final Future<CustomSchemeResponse?> Function(
InAppWebViewController controller, String scheme, String url) InAppWebViewController controller, String scheme, String url)?
onLoadResourceCustomScheme; onLoadResourceCustomScheme;
///Event fired when the [WebView] requests the host application to create a new window, ///Event fired when the [WebView] requests the host application to create a new window,
@ -160,8 +160,8 @@ abstract class WebView {
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onCreateWindow(android.webkit.WebView,%20boolean,%20boolean,%20android.os.Message) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onCreateWindow(android.webkit.WebView,%20boolean,%20boolean,%20android.os.Message)
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1536907-webview ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1536907-webview
final Future<bool> Function(InAppWebViewController controller, final Future<bool?> Function(InAppWebViewController controller,
CreateWindowRequest createWindowRequest) onCreateWindow; CreateWindowRequest createWindowRequest)? onCreateWindow;
///Event fired when the host application should close the given WebView and remove it from the view system if necessary. ///Event fired when the host application should close the given WebView and remove it from the view system if necessary.
///At this point, WebCore has stopped any loading in this window and has removed any cross-scripting ability in javascript. ///At this point, WebCore has stopped any loading in this window and has removed any cross-scripting ability in javascript.
@ -169,15 +169,15 @@ abstract class WebView {
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onCloseWindow(android.webkit.WebView) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onCloseWindow(android.webkit.WebView)
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1537390-webviewdidclose ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1537390-webviewdidclose
final void Function(InAppWebViewController controller) onCloseWindow; final void Function(InAppWebViewController controller)? onCloseWindow;
///Event fired when the JavaScript `window` object of the WebView has received focus. ///Event fired when the JavaScript `window` object of the WebView has received focus.
///This is the result of the `focus` JavaScript event applied to the `window` object. ///This is the result of the `focus` JavaScript event applied to the `window` object.
final void Function(InAppWebViewController controller) onWindowFocus; final void Function(InAppWebViewController controller)? onWindowFocus;
///Event fired when the JavaScript `window` object of the WebView has lost focus. ///Event fired when the JavaScript `window` object of the WebView has lost focus.
///This is the result of the `blur` JavaScript event applied to the `window` object. ///This is the result of the `blur` JavaScript event applied to the `window` object.
final void Function(InAppWebViewController controller) onWindowBlur; final void Function(InAppWebViewController controller)? onWindowBlur;
///Event fired when javascript calls the `alert()` method to display an alert dialog. ///Event fired when javascript calls the `alert()` method to display an alert dialog.
///If [JsAlertResponse.handledByClient] is `true`, the webview will assume that the client will handle the dialog. ///If [JsAlertResponse.handledByClient] is `true`, the webview will assume that the client will handle the dialog.
@ -187,8 +187,8 @@ abstract class WebView {
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsAlert(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20android.webkit.JsResult) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsAlert(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20android.webkit.JsResult)
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1537406-webview ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1537406-webview
final Future<JsAlertResponse> Function( final Future<JsAlertResponse?> Function(
InAppWebViewController controller, JsAlertRequest jsAlertRequest) InAppWebViewController controller, JsAlertRequest jsAlertRequest)?
onJsAlert; onJsAlert;
///Event fired when javascript calls the `confirm()` method to display a confirm dialog. ///Event fired when javascript calls the `confirm()` method to display a confirm dialog.
@ -199,8 +199,8 @@ abstract class WebView {
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsConfirm(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20android.webkit.JsResult) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsConfirm(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20android.webkit.JsResult)
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1536489-webview ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1536489-webview
final Future<JsConfirmResponse> Function( final Future<JsConfirmResponse?> Function(
InAppWebViewController controller, JsConfirmRequest jsConfirmRequest) InAppWebViewController controller, JsConfirmRequest jsConfirmRequest)?
onJsConfirm; onJsConfirm;
///Event fired when javascript calls the `prompt()` method to display a prompt dialog. ///Event fired when javascript calls the `prompt()` method to display a prompt dialog.
@ -211,8 +211,8 @@ abstract class WebView {
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsPrompt(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20java.lang.String,%20android.webkit.JsPromptResult) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsPrompt(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20java.lang.String,%20android.webkit.JsPromptResult)
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1538086-webview ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1538086-webview
final Future<JsPromptResponse> Function( final Future<JsPromptResponse?> Function(
InAppWebViewController controller, JsPromptRequest jsPromptRequest) InAppWebViewController controller, JsPromptRequest jsPromptRequest)?
onJsPrompt; onJsPrompt;
///Event fired when the WebView received an HTTP authentication request. The default behavior is to cancel the request. ///Event fired when the WebView received an HTTP authentication request. The default behavior is to cancel the request.
@ -222,8 +222,8 @@ abstract class WebView {
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedHttpAuthRequest(android.webkit.WebView,%20android.webkit.HttpAuthHandler,%20java.lang.String,%20java.lang.String) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedHttpAuthRequest(android.webkit.WebView,%20android.webkit.HttpAuthHandler,%20java.lang.String,%20java.lang.String)
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview
final Future<HttpAuthResponse> Function( final Future<HttpAuthResponse?> Function(
InAppWebViewController controller, HttpAuthChallenge challenge) InAppWebViewController controller, HttpAuthChallenge challenge)?
onReceivedHttpAuthRequest; onReceivedHttpAuthRequest;
///Event fired when the WebView need to perform server trust authentication (certificate validation). ///Event fired when the WebView need to perform server trust authentication (certificate validation).
@ -234,8 +234,8 @@ abstract class WebView {
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedSslError(android.webkit.WebView,%20android.webkit.SslErrorHandler,%20android.net.http.SslError) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedSslError(android.webkit.WebView,%20android.webkit.SslErrorHandler,%20android.net.http.SslError)
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview
final Future<ServerTrustAuthResponse> Function( final Future<ServerTrustAuthResponse?> Function(
InAppWebViewController controller, ServerTrustChallenge challenge) InAppWebViewController controller, ServerTrustChallenge challenge)?
onReceivedServerTrustAuthRequest; onReceivedServerTrustAuthRequest;
///Notify the host application to handle an SSL client certificate request. ///Notify the host application to handle an SSL client certificate request.
@ -248,8 +248,8 @@ abstract class WebView {
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedClientCertRequest(android.webkit.WebView,%20android.webkit.ClientCertRequest) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedClientCertRequest(android.webkit.WebView,%20android.webkit.ClientCertRequest)
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview
final Future<ClientCertResponse> Function( final Future<ClientCertResponse?> Function(
InAppWebViewController controller, ClientCertChallenge challenge) InAppWebViewController controller, ClientCertChallenge challenge)?
onReceivedClientCertRequest; onReceivedClientCertRequest;
///Event fired as find-on-page operations progress. ///Event fired as find-on-page operations progress.
@ -263,7 +263,7 @@ abstract class WebView {
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#setFindListener(android.webkit.WebView.FindListener) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#setFindListener(android.webkit.WebView.FindListener)
final void Function(InAppWebViewController controller, int activeMatchOrdinal, final void Function(InAppWebViewController controller, int activeMatchOrdinal,
int numberOfMatches, bool isDoneCounting) onFindResultReceived; int numberOfMatches, bool isDoneCounting)? onFindResultReceived;
///Event fired when an `XMLHttpRequest` is sent to a server. ///Event fired when an `XMLHttpRequest` is sent to a server.
///It gives the host application a chance to take control over the request before sending it. ///It gives the host application a chance to take control over the request before sending it.
@ -275,8 +275,8 @@ abstract class WebView {
///can inject javascript code right after the document element is created but before any other content is loaded, in Android the javascript code ///can inject javascript code right after the document element is created but before any other content is loaded, in Android the javascript code
///used to intercept ajax requests is loaded as soon as possible so it won't be instantaneous as iOS but just after some milliseconds (< ~100ms). ///used to intercept ajax requests is loaded as soon as possible so it won't be instantaneous as iOS but just after some milliseconds (< ~100ms).
///Inside the `window.addEventListener("flutterInAppWebViewPlatformReady")` event, the ajax requests will be intercept for sure. ///Inside the `window.addEventListener("flutterInAppWebViewPlatformReady")` event, the ajax requests will be intercept for sure.
final Future<AjaxRequest> Function( final Future<AjaxRequest?> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest) InAppWebViewController controller, AjaxRequest ajaxRequest)?
shouldInterceptAjaxRequest; shouldInterceptAjaxRequest;
///Event fired whenever the `readyState` attribute of an `XMLHttpRequest` changes. ///Event fired whenever the `readyState` attribute of an `XMLHttpRequest` changes.
@ -289,8 +289,8 @@ abstract class WebView {
///can inject javascript code right after the document element is created but before any other content is loaded, in Android the javascript code ///can inject javascript code right after the document element is created but before any other content is loaded, in Android the javascript code
///used to intercept ajax requests is loaded as soon as possible so it won't be instantaneous as iOS but just after some milliseconds (< ~100ms). ///used to intercept ajax requests is loaded as soon as possible so it won't be instantaneous as iOS but just after some milliseconds (< ~100ms).
///Inside the `window.addEventListener("flutterInAppWebViewPlatformReady")` event, the ajax requests will be intercept for sure. ///Inside the `window.addEventListener("flutterInAppWebViewPlatformReady")` event, the ajax requests will be intercept for sure.
final Future<AjaxRequestAction> Function( final Future<AjaxRequestAction?> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest) InAppWebViewController controller, AjaxRequest ajaxRequest)?
onAjaxReadyStateChange; onAjaxReadyStateChange;
///Event fired as an `XMLHttpRequest` progress. ///Event fired as an `XMLHttpRequest` progress.
@ -303,8 +303,8 @@ abstract class WebView {
///can inject javascript code right after the document element is created but before any other content is loaded, in Android the javascript code ///can inject javascript code right after the document element is created but before any other content is loaded, in Android the javascript code
///used to intercept ajax requests is loaded as soon as possible so it won't be instantaneous as iOS but just after some milliseconds (< ~100ms). ///used to intercept ajax requests is loaded as soon as possible so it won't be instantaneous as iOS but just after some milliseconds (< ~100ms).
///Inside the `window.addEventListener("flutterInAppWebViewPlatformReady")` event, the ajax requests will be intercept for sure. ///Inside the `window.addEventListener("flutterInAppWebViewPlatformReady")` event, the ajax requests will be intercept for sure.
final Future<AjaxRequestAction> Function( final Future<AjaxRequestAction?> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest) InAppWebViewController controller, AjaxRequest ajaxRequest)?
onAjaxProgress; onAjaxProgress;
///Event fired when a request is sent to a server through [Fetch API](https://developer.mozilla.org/it/docs/Web/API/Fetch_API). ///Event fired when a request is sent to a server through [Fetch API](https://developer.mozilla.org/it/docs/Web/API/Fetch_API).
@ -317,8 +317,8 @@ abstract class WebView {
///can inject javascript code right after the document element is created but before any other content is loaded, in Android the javascript code ///can inject javascript code right after the document element is created but before any other content is loaded, in Android the javascript code
///used to intercept fetch requests is loaded as soon as possible so it won't be instantaneous as iOS but just after some milliseconds (< ~100ms). ///used to intercept fetch requests is loaded as soon as possible so it won't be instantaneous as iOS but just after some milliseconds (< ~100ms).
///Inside the `window.addEventListener("flutterInAppWebViewPlatformReady")` event, the fetch requests will be intercept for sure. ///Inside the `window.addEventListener("flutterInAppWebViewPlatformReady")` event, the fetch requests will be intercept for sure.
final Future<FetchRequest> Function( final Future<FetchRequest?> Function(
InAppWebViewController controller, FetchRequest fetchRequest) InAppWebViewController controller, FetchRequest fetchRequest)?
shouldInterceptFetchRequest; shouldInterceptFetchRequest;
///Event fired when the host application updates its visited links database. ///Event fired when the host application updates its visited links database.
@ -332,7 +332,7 @@ abstract class WebView {
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#doUpdateVisitedHistory(android.webkit.WebView,%20java.lang.String,%20boolean) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#doUpdateVisitedHistory(android.webkit.WebView,%20java.lang.String,%20boolean)
final void Function( final void Function(
InAppWebViewController controller, String url, bool androidIsReload) InAppWebViewController controller, String? url, bool? androidIsReload)?
onUpdateVisitedHistory; onUpdateVisitedHistory;
///Event fired when `window.print()` is called from JavaScript side. ///Event fired when `window.print()` is called from JavaScript side.
@ -340,7 +340,7 @@ abstract class WebView {
///[url] represents the url on which is called. ///[url] represents the url on which is called.
/// ///
///**NOTE**: available on Android 21+. ///**NOTE**: available on Android 21+.
final void Function(InAppWebViewController controller, String url) onPrint; final void Function(InAppWebViewController controller, String? url)? onPrint;
///Event fired when an HTML element of the webview has been clicked and held. ///Event fired when an HTML element of the webview has been clicked and held.
/// ///
@ -350,21 +350,21 @@ abstract class WebView {
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uilongpressgesturerecognizer ///**Official iOS API**: https://developer.apple.com/documentation/uikit/uilongpressgesturerecognizer
final void Function(InAppWebViewController controller, final void Function(InAppWebViewController controller,
InAppWebViewHitTestResult hitTestResult) onLongPressHitTestResult; InAppWebViewHitTestResult hitTestResult)? onLongPressHitTestResult;
///Event fired when the current page has entered full screen mode. ///Event fired when the current page has entered full screen mode.
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onShowCustomView(android.view.View,%20android.webkit.WebChromeClient.CustomViewCallback) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onShowCustomView(android.view.View,%20android.webkit.WebChromeClient.CustomViewCallback)
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiwindow/1621621-didbecomevisiblenotification ///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiwindow/1621621-didbecomevisiblenotification
final void Function(InAppWebViewController controller) onEnterFullscreen; final void Function(InAppWebViewController controller)? onEnterFullscreen;
///Event fired when the current page has exited full screen mode. ///Event fired when the current page has exited full screen mode.
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onHideCustomView() ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onHideCustomView()
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiwindow/1621617-didbecomehiddennotification ///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiwindow/1621617-didbecomehiddennotification
final void Function(InAppWebViewController controller) onExitFullscreen; final void Function(InAppWebViewController controller)? onExitFullscreen;
///Called when the web view begins to receive web content. ///Called when the web view begins to receive web content.
/// ///
@ -376,7 +376,7 @@ abstract class WebView {
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onPageCommitVisible(android.webkit.WebView,%20java.lang.String) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onPageCommitVisible(android.webkit.WebView,%20java.lang.String)
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455635-webview ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455635-webview
final void Function(InAppWebViewController controller, String url) final void Function(InAppWebViewController controller, String? url)?
onPageCommitVisible; onPageCommitVisible;
///Event fired when a change in the document title occurred. ///Event fired when a change in the document title occurred.
@ -384,7 +384,7 @@ abstract class WebView {
///[title] represents the string containing the new title of the document. ///[title] represents the string containing the new title of the document.
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onReceivedTitle(android.webkit.WebView,%20java.lang.String) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onReceivedTitle(android.webkit.WebView,%20java.lang.String)
final void Function(InAppWebViewController controller, String title) final void Function(InAppWebViewController controller, String? title)?
onTitleChanged; onTitleChanged;
///Event fired when the webview notifies that a loading URL has been flagged by Safe Browsing. ///Event fired when the webview notifies that a loading URL has been flagged by Safe Browsing.
@ -397,8 +397,8 @@ abstract class WebView {
///**NOTE**: available only on Android 27+. ///**NOTE**: available only on Android 27+.
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onSafeBrowsingHit(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20int,%20android.webkit.SafeBrowsingResponse) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onSafeBrowsingHit(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20int,%20android.webkit.SafeBrowsingResponse)
final Future<SafeBrowsingResponse> Function(InAppWebViewController controller, final Future<SafeBrowsingResponse?> Function(InAppWebViewController controller,
String url, SafeBrowsingThreat threatType) androidOnSafeBrowsingHit; String url, SafeBrowsingThreat? threatType)? androidOnSafeBrowsingHit;
///Event fired when the WebView is requesting permission to access the specified resources and the permission currently isn't granted or denied. ///Event fired when the WebView is requesting permission to access the specified resources and the permission currently isn't granted or denied.
/// ///
@ -409,10 +409,10 @@ abstract class WebView {
///**NOTE**: available only on Android 23+. ///**NOTE**: available only on Android 23+.
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onPermissionRequest(android.webkit.PermissionRequest) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onPermissionRequest(android.webkit.PermissionRequest)
final Future<PermissionRequestResponse> Function( final Future<PermissionRequestResponse?> Function(
InAppWebViewController controller, InAppWebViewController controller,
String origin, String origin,
List<String> resources) androidOnPermissionRequest; List<String> resources)? androidOnPermissionRequest;
///Event that notifies the host application that web content from the specified origin is attempting to use the Geolocation API, but no permission state is currently set for that origin. ///Event that notifies the host application that web content from the specified origin is attempting to use the Geolocation API, but no permission state is currently set for that origin.
///Note that for applications targeting Android N and later SDKs (API level > `Build.VERSION_CODES.M`) this method is only called for requests originating from secure origins such as https. ///Note that for applications targeting Android N and later SDKs (API level > `Build.VERSION_CODES.M`) this method is only called for requests originating from secure origins such as https.
@ -423,8 +423,8 @@ abstract class WebView {
///**NOTE**: available only on Android. ///**NOTE**: available only on Android.
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onGeolocationPermissionsShowPrompt(java.lang.String,%20android.webkit.GeolocationPermissions.Callback) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onGeolocationPermissionsShowPrompt(java.lang.String,%20android.webkit.GeolocationPermissions.Callback)
final Future<GeolocationPermissionShowPromptResponse> Function( final Future<GeolocationPermissionShowPromptResponse?> Function(
InAppWebViewController controller, String origin) InAppWebViewController controller, String origin)?
androidOnGeolocationPermissionsShowPrompt; androidOnGeolocationPermissionsShowPrompt;
///Notify the host application that a request for Geolocation permissions, made with a previous call to [androidOnGeolocationPermissionsShowPrompt] has been canceled. ///Notify the host application that a request for Geolocation permissions, made with a previous call to [androidOnGeolocationPermissionsShowPrompt] has been canceled.
@ -433,7 +433,7 @@ abstract class WebView {
///**NOTE**: available only on Android. ///**NOTE**: available only on Android.
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onGeolocationPermissionsHidePrompt() ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onGeolocationPermissionsHidePrompt()
final void Function(InAppWebViewController controller) final void Function(InAppWebViewController controller)?
androidOnGeolocationPermissionsHidePrompt; androidOnGeolocationPermissionsHidePrompt;
///Notify the host application of a resource request and allow the application to return the data. ///Notify the host application of a resource request and allow the application to return the data.
@ -453,8 +453,8 @@ abstract class WebView {
///**Official Android API**: ///**Official Android API**:
///- https://developer.android.com/reference/android/webkit/WebViewClient#shouldInterceptRequest(android.webkit.WebView,%20android.webkit.WebResourceRequest) ///- https://developer.android.com/reference/android/webkit/WebViewClient#shouldInterceptRequest(android.webkit.WebView,%20android.webkit.WebResourceRequest)
///- https://developer.android.com/reference/android/webkit/WebViewClient#shouldInterceptRequest(android.webkit.WebView,%20java.lang.String) ///- https://developer.android.com/reference/android/webkit/WebViewClient#shouldInterceptRequest(android.webkit.WebView,%20java.lang.String)
final Future<WebResourceResponse> Function( final Future<WebResourceResponse?> Function(
InAppWebViewController controller, WebResourceRequest request) InAppWebViewController controller, WebResourceRequest request)?
androidShouldInterceptRequest; androidShouldInterceptRequest;
///Event called when the renderer currently associated with the WebView becomes unresponsive as a result of a long running blocking task such as the execution of JavaScript. ///Event called when the renderer currently associated with the WebView becomes unresponsive as a result of a long running blocking task such as the execution of JavaScript.
@ -475,8 +475,8 @@ abstract class WebView {
///**NOTE**: available only on Android 29+. ///**NOTE**: available only on Android 29+.
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewRenderProcessClient#onRenderProcessUnresponsive(android.webkit.WebView,%20android.webkit.WebViewRenderProcess) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewRenderProcessClient#onRenderProcessUnresponsive(android.webkit.WebView,%20android.webkit.WebViewRenderProcess)
final Future<WebViewRenderProcessAction> Function( final Future<WebViewRenderProcessAction?> Function(
InAppWebViewController controller, String url) InAppWebViewController controller, String? url)?
androidOnRenderProcessUnresponsive; androidOnRenderProcessUnresponsive;
///Event called once when an unresponsive renderer currently associated with the WebView becomes responsive. ///Event called once when an unresponsive renderer currently associated with the WebView becomes responsive.
@ -490,8 +490,8 @@ abstract class WebView {
///**NOTE**: available only on Android 29+. ///**NOTE**: available only on Android 29+.
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewRenderProcessClient#onRenderProcessResponsive(android.webkit.WebView,%20android.webkit.WebViewRenderProcess) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewRenderProcessClient#onRenderProcessResponsive(android.webkit.WebView,%20android.webkit.WebViewRenderProcess)
final Future<WebViewRenderProcessAction> Function( final Future<WebViewRenderProcessAction?> Function(
InAppWebViewController controller, String url) InAppWebViewController controller, String? url)?
androidOnRenderProcessResponsive; androidOnRenderProcessResponsive;
///Event fired when the given WebView's render process has exited. ///Event fired when the given WebView's render process has exited.
@ -504,7 +504,7 @@ abstract class WebView {
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onRenderProcessGone(android.webkit.WebView,%20android.webkit.RenderProcessGoneDetail) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onRenderProcessGone(android.webkit.WebView,%20android.webkit.RenderProcessGoneDetail)
final void Function( final void Function(
InAppWebViewController controller, RenderProcessGoneDetail detail) InAppWebViewController controller, RenderProcessGoneDetail detail)?
androidOnRenderProcessGone; androidOnRenderProcessGone;
///As the host application if the browser should resend data as the requested page was a result of a POST. The default is to not resend the data. ///As the host application if the browser should resend data as the requested page was a result of a POST. The default is to not resend the data.
@ -512,8 +512,8 @@ abstract class WebView {
///**NOTE**: available only on Android. ///**NOTE**: available only on Android.
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onFormResubmission(android.webkit.WebView,%20android.os.Message,%20android.os.Message) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onFormResubmission(android.webkit.WebView,%20android.os.Message,%20android.os.Message)
final Future<FormResubmissionAction> Function( final Future<FormResubmissionAction?> Function(
InAppWebViewController controller, String url) androidOnFormResubmission; InAppWebViewController controller, String? url)? androidOnFormResubmission;
///Event fired when the scale applied to the WebView has changed. ///Event fired when the scale applied to the WebView has changed.
/// ///
@ -525,7 +525,7 @@ abstract class WebView {
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onScaleChanged(android.webkit.WebView,%20float,%20float) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onScaleChanged(android.webkit.WebView,%20float,%20float)
final void Function( final void Function(
InAppWebViewController controller, double oldScale, double newScale) InAppWebViewController controller, double oldScale, double newScale)?
androidOnScaleChanged; androidOnScaleChanged;
///Event fired when there is a request to display and focus for this WebView. ///Event fired when there is a request to display and focus for this WebView.
@ -534,7 +534,7 @@ abstract class WebView {
///**NOTE**: available only on Android. ///**NOTE**: available only on Android.
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onRequestFocus(android.webkit.WebView) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onRequestFocus(android.webkit.WebView)
final void Function(InAppWebViewController controller) androidOnRequestFocus; final void Function(InAppWebViewController controller)? androidOnRequestFocus;
///Event fired when there is new favicon for the current page. ///Event fired when there is new favicon for the current page.
/// ///
@ -543,7 +543,7 @@ abstract class WebView {
///**NOTE**: available only on Android. ///**NOTE**: available only on Android.
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onReceivedIcon(android.webkit.WebView,%20android.graphics.Bitmap) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onReceivedIcon(android.webkit.WebView,%20android.graphics.Bitmap)
final void Function(InAppWebViewController controller, Uint8List icon) final void Function(InAppWebViewController controller, Uint8List icon)?
androidOnReceivedIcon; androidOnReceivedIcon;
///Event fired when there is an url for an apple-touch-icon. ///Event fired when there is an url for an apple-touch-icon.
@ -556,7 +556,7 @@ abstract class WebView {
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onReceivedTouchIconUrl(android.webkit.WebView,%20java.lang.String,%20boolean) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onReceivedTouchIconUrl(android.webkit.WebView,%20java.lang.String,%20boolean)
final void Function( final void Function(
InAppWebViewController controller, String url, bool precomposed) InAppWebViewController controller, String url, bool precomposed)?
androidOnReceivedTouchIconUrl; androidOnReceivedTouchIconUrl;
///Event fired when the client should display a dialog to confirm navigation away from the current page. ///Event fired when the client should display a dialog to confirm navigation away from the current page.
@ -572,9 +572,9 @@ abstract class WebView {
///**NOTE**: available only on Android. ///**NOTE**: available only on Android.
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsBeforeUnload(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20android.webkit.JsResult) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsBeforeUnload(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20android.webkit.JsResult)
final Future<JsBeforeUnloadResponse> Function( final Future<JsBeforeUnloadResponse?> Function(
InAppWebViewController controller, InAppWebViewController controller,
JsBeforeUnloadRequest jsBeforeUnloadRequest) androidOnJsBeforeUnload; JsBeforeUnloadRequest jsBeforeUnloadRequest)? androidOnJsBeforeUnload;
///Event fired when a request to automatically log in the user has been processed. ///Event fired when a request to automatically log in the user has been processed.
/// ///
@ -584,7 +584,7 @@ abstract class WebView {
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedLoginRequest(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20java.lang.String) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedLoginRequest(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20java.lang.String)
final void Function( final void Function(
InAppWebViewController controller, LoginRequest loginRequest) InAppWebViewController controller, LoginRequest loginRequest)?
androidOnReceivedLoginRequest; androidOnReceivedLoginRequest;
///Invoked when the web view's web content process is terminated. ///Invoked when the web view's web content process is terminated.
@ -592,7 +592,7 @@ abstract class WebView {
///**NOTE**: available only on iOS. ///**NOTE**: available only on iOS.
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455639-webviewwebcontentprocessdidtermi ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455639-webviewwebcontentprocessdidtermi
final void Function(InAppWebViewController controller) final void Function(InAppWebViewController controller)?
iosOnWebContentProcessDidTerminate; iosOnWebContentProcessDidTerminate;
///Called when a web view receives a server redirect. ///Called when a web view receives a server redirect.
@ -600,26 +600,26 @@ abstract class WebView {
///**NOTE**: available only on iOS. ///**NOTE**: available only on iOS.
/// ///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455627-webview ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455627-webview
final void Function(InAppWebViewController controller) final void Function(InAppWebViewController controller)?
iosOnDidReceiveServerRedirectForProvisionalNavigation; iosOnDidReceiveServerRedirectForProvisionalNavigation;
///Initial url that will be loaded. ///Initial url that will be loaded.
final String initialUrl; final String? initialUrl;
///Initial asset file that will be loaded. See [InAppWebViewController.loadFile] for explanation. ///Initial asset file that will be loaded. See [InAppWebViewController.loadFile] for explanation.
final String initialFile; final String? initialFile;
///Initial [InAppWebViewInitialData] that will be loaded. ///Initial [InAppWebViewInitialData] that will be loaded.
final InAppWebViewInitialData initialData; final InAppWebViewInitialData? initialData;
///Initial headers that will be used. ///Initial headers that will be used.
final Map<String, String> initialHeaders; final Map<String, String>? initialHeaders;
///Initial options that will be used. ///Initial options that will be used.
final InAppWebViewGroupOptions initialOptions; final InAppWebViewGroupOptions? initialOptions;
///Context menu which contains custom menu items to be shown when [ContextMenu] is presented. ///Context menu which contains custom menu items to be shown when [ContextMenu] is presented.
final ContextMenu contextMenu; final ContextMenu? contextMenu;
WebView( WebView(
{this.windowId, {this.windowId,

View File

@ -14,7 +14,7 @@ class WebViewOptions {
} }
static WebViewOptions fromMap(Map<String, dynamic> map) { static WebViewOptions fromMap(Map<String, dynamic> map) {
return null; return new WebViewOptions();
} }
WebViewOptions copy() { WebViewOptions copy() {
@ -37,7 +37,7 @@ class BrowserOptions {
} }
static BrowserOptions fromMap(Map<String, dynamic> map) { static BrowserOptions fromMap(Map<String, dynamic> map) {
return null; return new BrowserOptions();
} }
BrowserOptions copy() { BrowserOptions copy() {
@ -60,7 +60,7 @@ class ChromeSafariBrowserOptions {
} }
static ChromeSafariBrowserOptions fromMap(Map<String, dynamic> map) { static ChromeSafariBrowserOptions fromMap(Map<String, dynamic> map) {
return null; return new ChromeSafariBrowserOptions();
} }
ChromeSafariBrowserOptions copy() { ChromeSafariBrowserOptions copy() {
@ -105,12 +105,6 @@ class InAppWebViewOptions
///Set to `true` to enable JavaScript. The default value is `true`. ///Set to `true` to enable JavaScript. The default value is `true`.
bool javaScriptEnabled; bool javaScriptEnabled;
///Enables debugging of web contents (HTML / CSS / JavaScript) loaded into any WebViews of this application.
///This flag can be enabled in order to facilitate debugging of web layouts and JavaScript code running inside WebViews. The default is `false`.
///
///**NOTE**: on iOS the debugging mode is always enabled.
bool debuggingEnabled;
///Set to `true` to allow JavaScript open windows without user interaction. The default value is `false`. ///Set to `true` to allow JavaScript open windows without user interaction. The default value is `false`.
bool javaScriptCanOpenWindowsAutomatically; bool javaScriptCanOpenWindowsAutomatically;
@ -120,7 +114,7 @@ class InAppWebViewOptions
bool mediaPlaybackRequiresUserGesture; bool mediaPlaybackRequiresUserGesture;
///Sets the minimum font size. The default value is `8` for Android, `0` for iOS. ///Sets the minimum font size. The default value is `8` for Android, `0` for iOS.
int minimumFontSize; int? minimumFontSize;
///Define whether the vertical scrollbar should be drawn or not. The default value is `true`. ///Define whether the vertical scrollbar should be drawn or not. The default value is `true`.
bool verticalScrollBarEnabled; bool verticalScrollBarEnabled;
@ -141,7 +135,7 @@ class InAppWebViewOptions
///Sets the content mode that the WebView needs to use when loading and rendering a webpage. The default value is [UserPreferredContentMode.RECOMMENDED]. ///Sets the content mode that the WebView needs to use when loading and rendering a webpage. The default value is [UserPreferredContentMode.RECOMMENDED].
/// ///
///**NOTE**: available on iOS 13.0+. ///**NOTE**: available on iOS 13.0+.
UserPreferredContentMode preferredContentMode; 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 [shouldInterceptAjaxRequest] event. The default value is `false`.
bool useShouldInterceptAjaxRequest; bool useShouldInterceptAjaxRequest;
@ -152,6 +146,8 @@ class InAppWebViewOptions
///Set to `true` to open a browser window with incognito mode. The default value is `false`. ///Set to `true` to open a browser window with incognito mode. The default value is `false`.
/// ///
///**NOTE**: available on iOS 9.0+. ///**NOTE**: available on iOS 9.0+.
///On Android, by setting this option to `true`, it will clear all the cookies of all WebView instances,
///because there isn't any way to make the website data store non-persistent for the specific WebView instance such as on iOS.
bool incognito; bool incognito;
///Sets whether WebView should use browser caching. The default value is `true`. ///Sets whether WebView should use browser caching. The default value is `true`.
@ -174,6 +170,28 @@ class InAppWebViewOptions
///Set to `false` if the WebView should not support zooming using its on-screen zoom controls and gestures. The default value is `true`. ///Set to `false` if the WebView should not support zooming using its on-screen zoom controls and gestures. The default value is `true`.
bool supportZoom; bool supportZoom;
///Sets whether cross-origin requests in the context of a file scheme URL should be allowed to access content from other file scheme URLs.
///Note that some accesses such as image HTML elements don't follow same-origin rules and aren't affected by this setting.
///
///Don't enable this setting if you open files that may be created or altered by external sources.
///Enabling this setting allows malicious scripts loaded in a `file://` context to access arbitrary local files including WebView cookies and app private data.
///
///Note that the value of this setting is ignored if the value of [allowUniversalAccessFromFileURLs] is `true`.
///
///The default value is `false`.
bool allowFileAccessFromFileURLs;
///Sets whether cross-origin requests in the context of a file scheme URL should be allowed to access content from any origin.
///This includes access to content from other file scheme URLs or web contexts.
///Note that some access such as image HTML elements doesn't follow same-origin rules and isn't affected by this setting.
///
///Don't enable this setting if you open files that may be created or altered by external sources.
///Enabling this setting allows malicious scripts loaded in a `file://` context to launch cross-site scripting attacks,
///either accessing arbitrary local files including WebView cookies, app private data or even credentials used on arbitrary web sites.
///
///The default value is `false`.
bool allowUniversalAccessFromFileURLs;
InAppWebViewOptions( InAppWebViewOptions(
{this.useShouldOverrideUrlLoading = false, {this.useShouldOverrideUrlLoading = false,
this.useOnLoadResource = false, this.useOnLoadResource = false,
@ -182,7 +200,6 @@ class InAppWebViewOptions
this.userAgent = "", this.userAgent = "",
this.applicationNameForUserAgent = "", this.applicationNameForUserAgent = "",
this.javaScriptEnabled = true, this.javaScriptEnabled = true,
this.debuggingEnabled = false,
this.javaScriptCanOpenWindowsAutomatically = false, this.javaScriptCanOpenWindowsAutomatically = false,
this.mediaPlaybackRequiresUserGesture = true, this.mediaPlaybackRequiresUserGesture = true,
this.minimumFontSize, this.minimumFontSize,
@ -199,7 +216,9 @@ class InAppWebViewOptions
this.disableVerticalScroll = false, this.disableVerticalScroll = false,
this.disableHorizontalScroll = false, this.disableHorizontalScroll = false,
this.disableContextMenu = false, this.disableContextMenu = false,
this.supportZoom = true}) { this.supportZoom = true,
this.allowFileAccessFromFileURLs = false,
this.allowUniversalAccessFromFileURLs = false}) {
if (this.minimumFontSize == null) if (this.minimumFontSize == null)
this.minimumFontSize = defaultTargetPlatform == TargetPlatform.android ? 8 : 0; this.minimumFontSize = defaultTargetPlatform == TargetPlatform.android ? 8 : 0;
assert(!this.resourceCustomSchemes.contains("http") && assert(!this.resourceCustomSchemes.contains("http") &&
@ -221,7 +240,6 @@ class InAppWebViewOptions
"userAgent": userAgent, "userAgent": userAgent,
"applicationNameForUserAgent": applicationNameForUserAgent, "applicationNameForUserAgent": applicationNameForUserAgent,
"javaScriptEnabled": javaScriptEnabled, "javaScriptEnabled": javaScriptEnabled,
"debuggingEnabled": debuggingEnabled,
"javaScriptCanOpenWindowsAutomatically": "javaScriptCanOpenWindowsAutomatically":
javaScriptCanOpenWindowsAutomatically, javaScriptCanOpenWindowsAutomatically,
"mediaPlaybackRequiresUserGesture": mediaPlaybackRequiresUserGesture, "mediaPlaybackRequiresUserGesture": mediaPlaybackRequiresUserGesture,
@ -238,13 +256,15 @@ class InAppWebViewOptions
"disableVerticalScroll": disableVerticalScroll, "disableVerticalScroll": disableVerticalScroll,
"disableHorizontalScroll": disableHorizontalScroll, "disableHorizontalScroll": disableHorizontalScroll,
"disableContextMenu": disableContextMenu, "disableContextMenu": disableContextMenu,
"supportZoom": supportZoom "supportZoom": supportZoom,
"allowFileAccessFromFileURLs": allowFileAccessFromFileURLs,
"allowUniversalAccessFromFileURLs": allowUniversalAccessFromFileURLs
}; };
} }
static InAppWebViewOptions fromMap(Map<String, dynamic> map) { static InAppWebViewOptions fromMap(Map<String, dynamic> map) {
List<ContentBlocker> contentBlockers = []; List<ContentBlocker> contentBlockers = [];
List<dynamic> contentBlockersMapList = map["contentBlockers"]; List<dynamic>? contentBlockersMapList = map["contentBlockers"];
if (contentBlockersMapList != null) { if (contentBlockersMapList != null) {
contentBlockersMapList.forEach((contentBlocker) { contentBlockersMapList.forEach((contentBlocker) {
contentBlockers.add(ContentBlocker.fromMap( contentBlockers.add(ContentBlocker.fromMap(
@ -261,7 +281,6 @@ class InAppWebViewOptions
options.userAgent = map["userAgent"]; options.userAgent = map["userAgent"];
options.applicationNameForUserAgent = map["applicationNameForUserAgent"]; options.applicationNameForUserAgent = map["applicationNameForUserAgent"];
options.javaScriptEnabled = map["javaScriptEnabled"]; options.javaScriptEnabled = map["javaScriptEnabled"];
options.debuggingEnabled = map["debuggingEnabled"];
options.javaScriptCanOpenWindowsAutomatically = options.javaScriptCanOpenWindowsAutomatically =
map["javaScriptCanOpenWindowsAutomatically"]; map["javaScriptCanOpenWindowsAutomatically"];
options.mediaPlaybackRequiresUserGesture = options.mediaPlaybackRequiresUserGesture =
@ -284,6 +303,8 @@ class InAppWebViewOptions
options.disableHorizontalScroll = map["disableHorizontalScroll"]; options.disableHorizontalScroll = map["disableHorizontalScroll"];
options.disableContextMenu = map["disableContextMenu"]; options.disableContextMenu = map["disableContextMenu"];
options.supportZoom = map["supportZoom"]; options.supportZoom = map["supportZoom"];
options.allowFileAccessFromFileURLs = map["allowFileAccessFromFileURLs"];
options.allowUniversalAccessFromFileURLs = map["allowUniversalAccessFromFileURLs"];
return options; return options;
} }
@ -339,28 +360,18 @@ class AndroidInAppWebViewOptions
///Configures the WebView's behavior when a secure origin attempts to load a resource from an insecure origin. ///Configures the WebView's behavior when a secure origin attempts to load a resource from an insecure origin.
/// ///
///**NOTE**: available on Android 21+. ///**NOTE**: available on Android 21+.
AndroidMixedContentMode mixedContentMode; AndroidMixedContentMode? mixedContentMode;
///Enables or disables content URL access within WebView. Content URL access allows WebView to load content from a content provider installed in the system. The default value is `true`. ///Enables or disables content URL access within WebView. Content URL access allows WebView to load content from a content provider installed in the system. The default value is `true`.
bool allowContentAccess; bool allowContentAccess;
///Enables or disables file access within WebView. Note that this enables or disables file system access only. ///Enables or disables file access within WebView. Note that this enables or disables file system access only.
///Assets and resources are still accessible using \file:///android_asset` and `file:///android_res`. The default value is `true`. ///Assets and resources are still accessible using `file:///android_asset` and `file:///android_res`. The default value is `true`.
bool allowFileAccess; bool allowFileAccess;
///Sets whether JavaScript running in the context of a file scheme URL should be allowed to access content from other file scheme URLs.
///Note that the value of this setting is ignored if the value of [allowFileAccessFromFileURLs] is `true`.
///Note too, that this setting affects only JavaScript access to file scheme resources. The default value is `false`.
bool allowFileAccessFromFileURLs;
///Sets whether JavaScript running in the context of a file scheme URL should be allowed to access content from any origin.
///Note that this setting affects only JavaScript access to file scheme resources.
///This includes access to content from other file scheme URLs. The default value is `false`.
bool allowUniversalAccessFromFileURLs;
///Sets the path to the Application Caches files. In order for the Application Caches API to be enabled, this option must be set a path to which the application can write. ///Sets the path to the Application Caches files. In order for the Application Caches API to be enabled, this option must be set a path to which the application can write.
///This option is used one time: repeated calls are ignored. ///This option is used one time: repeated calls are ignored.
String appCachePath; String? appCachePath;
///Sets whether the WebView should not load image resources from the network (resources accessed via http and https URI schemes). The default value is `false`. ///Sets whether the WebView should not load image resources from the network (resources accessed via http and https URI schemes). The default value is `false`.
bool blockNetworkImage; bool blockNetworkImage;
@ -370,7 +381,7 @@ class AndroidInAppWebViewOptions
///Overrides the way the cache is used. The way the cache is used is based on the navigation type. For a normal page load, the cache is checked and content is re-validated as needed. ///Overrides the way the cache is used. The way the cache is used is based on the navigation type. For a normal page load, the cache is checked and content is re-validated as needed.
///When navigating back, content is not revalidated, instead the content is just retrieved from the cache. The default value is [AndroidCacheMode.LOAD_DEFAULT]. ///When navigating back, content is not revalidated, instead the content is just retrieved from the cache. The default value is [AndroidCacheMode.LOAD_DEFAULT].
AndroidCacheMode cacheMode; AndroidCacheMode? cacheMode;
///Sets the cursive font family name. The default value is `"cursive"`. ///Sets the cursive font family name. The default value is `"cursive"`.
String cursiveFontFamily; String cursiveFontFamily;
@ -387,7 +398,7 @@ class AndroidInAppWebViewOptions
///Disables the action mode menu items according to menuItems flag. ///Disables the action mode menu items according to menuItems flag.
/// ///
///**NOTE**: available on Android 24+. ///**NOTE**: available on Android 24+.
AndroidActionModeMenuItem disabledActionModeMenuItems; AndroidActionModeMenuItem? disabledActionModeMenuItems;
///Sets the fantasy font family name. The default value is `"fantasy"`. ///Sets the fantasy font family name. The default value is `"fantasy"`.
String fantasyFontFamily; String fantasyFontFamily;
@ -398,13 +409,13 @@ class AndroidInAppWebViewOptions
///Set the force dark mode for this WebView. The default value is [AndroidForceDark.FORCE_DARK_OFF]. ///Set the force dark mode for this WebView. The default value is [AndroidForceDark.FORCE_DARK_OFF].
/// ///
///**NOTE**: available on Android 29+. ///**NOTE**: available on Android 29+.
AndroidForceDark forceDark; AndroidForceDark? forceDark;
///Sets whether Geolocation API is enabled. The default value is `true`. ///Sets whether Geolocation API is enabled. The default value is `true`.
bool geolocationEnabled; bool geolocationEnabled;
///Sets the underlying layout algorithm. This will cause a re-layout of the WebView. ///Sets the underlying layout algorithm. This will cause a re-layout of the WebView.
AndroidLayoutAlgorithm layoutAlgorithm; AndroidLayoutAlgorithm? layoutAlgorithm;
///Sets whether the WebView loads pages in overview mode, that is, zooms out the content to fit on screen by width. ///Sets whether the WebView loads pages in overview mode, that is, zooms out the content to fit on screen by width.
///This setting is taken into account when the content width is greater than the width of the WebView control, for example, when [useWideViewPort] is enabled. ///This setting is taken into account when the content width is greater than the width of the WebView control, for example, when [useWideViewPort] is enabled.
@ -468,7 +479,7 @@ class AndroidInAppWebViewOptions
///Regular expression used by [shouldOverrideUrlLoading] event to cancel navigation requests for frames that are not the main frame. ///Regular expression used by [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. ///If the url request of a subframe matches the regular expression, then the request of that subframe is canceled.
String regexToCancelSubFramesLoading; String? regexToCancelSubFramesLoading;
///Set to `true` to enable Flutter's new Hybrid Composition. The default value is `false`. ///Set to `true` to enable Flutter's new Hybrid Composition. The default value is `false`.
///Hybrid Composition is supported starting with Flutter v1.20+. ///Hybrid Composition is supported starting with Flutter v1.20+.
@ -486,11 +497,11 @@ class AndroidInAppWebViewOptions
///Sets the WebView's over-scroll mode. ///Sets the WebView's over-scroll mode.
///Setting the over-scroll mode of a WebView will have an effect only if the WebView is capable of scrolling. ///Setting the over-scroll mode of a WebView will have an effect only if the WebView is capable of scrolling.
///The default value is [AndroidOverScrollMode.OVER_SCROLL_IF_CONTENT_SCROLLS]. ///The default value is [AndroidOverScrollMode.OVER_SCROLL_IF_CONTENT_SCROLLS].
AndroidOverScrollMode overScrollMode; AndroidOverScrollMode? overScrollMode;
///Informs WebView of the network state. ///Informs WebView of the network state.
///This is used to set the JavaScript property `window.navigator.isOnline` and generates the online/offline event as specified in HTML5, sec. 5.7.7. ///This is used to set the JavaScript property `window.navigator.isOnline` and generates the online/offline event as specified in HTML5, sec. 5.7.7.
bool networkAvailable; bool? networkAvailable;
///Specifies the style of the scrollbars. The scrollbars can be overlaid or inset. ///Specifies the style of the scrollbars. The scrollbars can be overlaid or inset.
///When inset, they add to the padding of the view. And the scrollbars can be drawn inside the padding area or on the edge of the view. ///When inset, they add to the padding of the view. And the scrollbars can be drawn inside the padding area or on the edge of the view.
@ -498,28 +509,28 @@ class AndroidInAppWebViewOptions
///you can use SCROLLBARS_INSIDE_OVERLAY or SCROLLBARS_INSIDE_INSET. If you want them to appear at the edge of the view, ignoring the padding, ///you can use SCROLLBARS_INSIDE_OVERLAY or SCROLLBARS_INSIDE_INSET. If you want them to appear at the edge of the view, ignoring the padding,
///then you can use SCROLLBARS_OUTSIDE_OVERLAY or SCROLLBARS_OUTSIDE_INSET. ///then you can use SCROLLBARS_OUTSIDE_OVERLAY or SCROLLBARS_OUTSIDE_INSET.
///The default value is [AndroidScrollBarStyle.SCROLLBARS_INSIDE_OVERLAY]. ///The default value is [AndroidScrollBarStyle.SCROLLBARS_INSIDE_OVERLAY].
AndroidScrollBarStyle scrollBarStyle; AndroidScrollBarStyle? scrollBarStyle;
///Sets the position of the vertical scroll bar. ///Sets the position of the vertical scroll bar.
///The default value is [AndroidVerticalScrollbarPosition.SCROLLBAR_POSITION_DEFAULT]. ///The default value is [AndroidVerticalScrollbarPosition.SCROLLBAR_POSITION_DEFAULT].
AndroidVerticalScrollbarPosition verticalScrollbarPosition; AndroidVerticalScrollbarPosition? verticalScrollbarPosition;
///Defines the delay in milliseconds that a scrollbar waits before fade out. ///Defines the delay in milliseconds that a scrollbar waits before fade out.
int scrollBarDefaultDelayBeforeFade; int? scrollBarDefaultDelayBeforeFade;
///Defines whether scrollbars will fade when the view is not scrolling. ///Defines whether scrollbars will fade when the view is not scrolling.
///The default value is `true`. ///The default value is `true`.
bool scrollbarFadingEnabled; bool scrollbarFadingEnabled;
///Defines the scrollbar fade duration in milliseconds. ///Defines the scrollbar fade duration in milliseconds.
int scrollBarFadeDuration; int? scrollBarFadeDuration;
///Sets the renderer priority policy for this WebView. ///Sets the renderer priority policy for this WebView.
RendererPriorityPolicy rendererPriorityPolicy; RendererPriorityPolicy? rendererPriorityPolicy;
///Sets whether the default Android error page should be disabled. ///Sets whether the default Android error page should be disabled.
///The default value is `false`. ///The default value is `false`.
bool disableDefaultErrorPage; bool? disableDefaultErrorPage;
AndroidInAppWebViewOptions({ AndroidInAppWebViewOptions({
this.textZoom = 100, this.textZoom = 100,
@ -533,8 +544,6 @@ class AndroidInAppWebViewOptions
this.mixedContentMode, this.mixedContentMode,
this.allowContentAccess = true, this.allowContentAccess = true,
this.allowFileAccess = true, this.allowFileAccess = true,
this.allowFileAccessFromFileURLs = false,
this.allowUniversalAccessFromFileURLs = false,
this.appCachePath, this.appCachePath,
this.blockNetworkImage = false, this.blockNetworkImage = false,
this.blockNetworkLoads = false, this.blockNetworkLoads = false,
@ -592,8 +601,6 @@ class AndroidInAppWebViewOptions
"mixedContentMode": mixedContentMode?.toValue(), "mixedContentMode": mixedContentMode?.toValue(),
"allowContentAccess": allowContentAccess, "allowContentAccess": allowContentAccess,
"allowFileAccess": allowFileAccess, "allowFileAccess": allowFileAccess,
"allowFileAccessFromFileURLs": allowFileAccessFromFileURLs,
"allowUniversalAccessFromFileURLs": allowUniversalAccessFromFileURLs,
"appCachePath": appCachePath, "appCachePath": appCachePath,
"blockNetworkImage": blockNetworkImage, "blockNetworkImage": blockNetworkImage,
"blockNetworkLoads": blockNetworkLoads, "blockNetworkLoads": blockNetworkLoads,
@ -651,9 +658,6 @@ class AndroidInAppWebViewOptions
AndroidMixedContentMode.fromValue(map["mixedContentMode"]); AndroidMixedContentMode.fromValue(map["mixedContentMode"]);
options.allowContentAccess = map["allowContentAccess"]; options.allowContentAccess = map["allowContentAccess"];
options.allowFileAccess = map["allowFileAccess"]; options.allowFileAccess = map["allowFileAccess"];
options.allowFileAccessFromFileURLs = map["allowFileAccessFromFileURLs"];
options.allowUniversalAccessFromFileURLs =
map["allowUniversalAccessFromFileURLs"];
options.appCachePath = map["appCachePath"]; options.appCachePath = map["appCachePath"];
options.blockNetworkImage = map["blockNetworkImage"]; options.blockNetworkImage = map["blockNetworkImage"];
options.blockNetworkLoads = map["blockNetworkLoads"]; options.blockNetworkLoads = map["blockNetworkLoads"];
@ -899,8 +903,11 @@ class IOSInAppWebViewOptions
List<IOSWKDataDetectorTypes> dataDetectorTypes = []; List<IOSWKDataDetectorTypes> dataDetectorTypes = [];
List<String> dataDetectorTypesList = List<String> dataDetectorTypesList =
List<String>.from(map["dataDetectorTypes"] ?? []); List<String>.from(map["dataDetectorTypes"] ?? []);
dataDetectorTypesList.forEach((dataDetectorType) { dataDetectorTypesList.forEach((dataDetectorTypeValue) {
dataDetectorTypes.add(IOSWKDataDetectorTypes.fromValue(dataDetectorType)); var dataDetectorType = IOSWKDataDetectorTypes.fromValue(dataDetectorTypeValue);
if (dataDetectorType != null) {
dataDetectorTypes.add(dataDetectorType);
}
}); });
IOSInAppWebViewOptions options = IOSInAppWebViewOptions(); IOSInAppWebViewOptions options = IOSInAppWebViewOptions();
@ -920,7 +927,7 @@ class IOSInAppWebViewOptions
options.isFraudulentWebsiteWarningEnabled = options.isFraudulentWebsiteWarningEnabled =
map["isFraudulentWebsiteWarningEnabled"]; map["isFraudulentWebsiteWarningEnabled"];
options.selectionGranularity = options.selectionGranularity =
IOSWKSelectionGranularity.fromValue(map["selectionGranularity"]); IOSWKSelectionGranularity.fromValue(map["selectionGranularity"])!;
options.dataDetectorTypes = dataDetectorTypes; options.dataDetectorTypes = dataDetectorTypes;
options.sharedCookiesEnabled = map["sharedCookiesEnabled"]; options.sharedCookiesEnabled = map["sharedCookiesEnabled"];
options.automaticallyAdjustsScrollIndicatorInsets = options.automaticallyAdjustsScrollIndicatorInsets =
@ -928,7 +935,7 @@ class IOSInAppWebViewOptions
options.accessibilityIgnoresInvertColors = options.accessibilityIgnoresInvertColors =
map["accessibilityIgnoresInvertColors"]; map["accessibilityIgnoresInvertColors"];
options.decelerationRate = options.decelerationRate =
IOSUIScrollViewDecelerationRate.fromValue(map["decelerationRate"]); IOSUIScrollViewDecelerationRate.fromValue(map["decelerationRate"])!;
options.alwaysBounceVertical = map["alwaysBounceVertical"]; options.alwaysBounceVertical = map["alwaysBounceVertical"];
options.alwaysBounceHorizontal = map["alwaysBounceHorizontal"]; options.alwaysBounceHorizontal = map["alwaysBounceHorizontal"];
options.scrollsToTop = map["scrollsToTop"]; options.scrollsToTop = map["scrollsToTop"];
@ -937,7 +944,7 @@ class IOSInAppWebViewOptions
options.minimumZoomScale = map["minimumZoomScale"]; options.minimumZoomScale = map["minimumZoomScale"];
options.contentInsetAdjustmentBehavior = options.contentInsetAdjustmentBehavior =
IOSUIScrollViewContentInsetAdjustmentBehavior.fromValue( IOSUIScrollViewContentInsetAdjustmentBehavior.fromValue(
map["contentInsetAdjustmentBehavior"]); map["contentInsetAdjustmentBehavior"])!;
return options; return options;
} }
@ -1127,9 +1134,9 @@ class IOSInAppBrowserOptions implements BrowserOptions, IosOptions {
options.closeButtonCaption = map["closeButtonCaption"]; options.closeButtonCaption = map["closeButtonCaption"];
options.closeButtonColor = map["closeButtonColor"]; options.closeButtonColor = map["closeButtonColor"];
options.presentationStyle = options.presentationStyle =
IOSUIModalPresentationStyle.fromValue(map["presentationStyle"]); IOSUIModalPresentationStyle.fromValue(map["presentationStyle"])!;
options.transitionStyle = options.transitionStyle =
IOSUIModalTransitionStyle.fromValue(map["transitionStyle"]); IOSUIModalTransitionStyle.fromValue(map["transitionStyle"])!;
options.spinner = map["spinner"]; options.spinner = map["spinner"];
return options; return options;
} }
@ -1173,7 +1180,7 @@ class AndroidChromeCustomTabsOptions
///value of null, all components in all applications will considered. ///value of null, all components in all applications will considered.
///If non-null, the Intent can only match the components in the given ///If non-null, the Intent can only match the components in the given
///application package. ///application package.
String packageName; String? packageName;
///Set to `true` to enable Keep Alive. The default value is `false`. ///Set to `true` to enable Keep Alive. The default value is `false`.
bool keepAliveEnabled; bool keepAliveEnabled;
@ -1285,13 +1292,13 @@ class IOSSafariOptions implements ChromeSafariBrowserOptions, IosOptions {
options.entersReaderIfAvailable = map["entersReaderIfAvailable"]; options.entersReaderIfAvailable = map["entersReaderIfAvailable"];
options.barCollapsingEnabled = map["barCollapsingEnabled"]; options.barCollapsingEnabled = map["barCollapsingEnabled"];
options.dismissButtonStyle = options.dismissButtonStyle =
IOSSafariDismissButtonStyle.fromValue(map["dismissButtonStyle"]); IOSSafariDismissButtonStyle.fromValue(map["dismissButtonStyle"])!;
options.preferredBarTintColor = map["preferredBarTintColor"]; options.preferredBarTintColor = map["preferredBarTintColor"];
options.preferredControlTintColor = map["preferredControlTintColor"]; options.preferredControlTintColor = map["preferredControlTintColor"];
options.presentationStyle = options.presentationStyle =
IOSUIModalPresentationStyle.fromValue(map["presentationStyle"]); IOSUIModalPresentationStyle.fromValue(map["presentationStyle"])!;
options.transitionStyle = options.transitionStyle =
IOSUIModalTransitionStyle.fromValue(map["transitionStyle"]); IOSUIModalTransitionStyle.fromValue(map["transitionStyle"])!;
return options; return options;
} }

View File

@ -168,7 +168,6 @@ app.post("/test-ajax-post", (req, res) => {
res.send(JSON.stringify({ res.send(JSON.stringify({
"firstname": req.body.firstname, "firstname": req.body.firstname,
"lastname": req.body.lastname, "lastname": req.body.lastname,
"fullname": req.body.firstname + " " + req.body.lastname,
})) }))
res.end() res.end()
}) })

View File

@ -1,17 +1,22 @@
name: flutter_inappwebview 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. 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.0.0 version: 5.0.0-nullsafety.0
homepage: https://github.com/pichillilorenzo/flutter_inappwebview homepage: https://github.com/pichillilorenzo/flutter_inappwebview
environment: environment:
sdk: ">=2.7.0 <3.0.0" sdk: ">=2.12.0-0 <3.0.0"
flutter: ">=1.12.13+hotfix.5" flutter: ">=1.22.0"
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
uuid: ^2.0.0 uuid: ^3.0.0-nullsafety.0
mime: ^0.9.6+2 mime: ^1.0.0-nullsafety.0
dev_dependencies:
flutter_test:
sdk: flutter
pedantic: ^1.10.0-nullsafety.1
# For information on the generic Dart part of this file, see the # For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec # following page: https://www.dartlang.org/tools/pub/pubspec

View File

@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
# on linux/macOS local IP can be found using $(ipconfig getifaddr en0) # on linux/macOS local IP can be found using $(ipconfig getifaddr en0)
export NODE_SERVER_IP=$1 export NODE_SERVER_IP=$1
dart tool/env.dart dart tool/env.dart
@ -7,5 +8,5 @@ node index.js &
flutter clean flutter clean
cd ../example cd ../example
flutter clean flutter clean
flutter driver -t test_driver/app.dart flutter driver --driver=test_driver/integration_test.dart --target=integration_test/webview_flutter_test.dart
kill $(jobs -p) kill $(jobs -p)

View File

@ -7,6 +7,6 @@ Future<void> main() async {
'NODE_SERVER_IP': Platform.environment['NODE_SERVER_IP'], 'NODE_SERVER_IP': Platform.environment['NODE_SERVER_IP'],
}; };
final filename = 'example/test_driver/.env.dart'; final filename = 'example/integration_test/.env.dart';
await File(filename).writeAsString('final environment = ${json.encode(config)};'); await File(filename).writeAsString('final environment = ${json.encode(config)};');
} }