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 `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 "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 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 "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 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 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

439
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
- Dart sdk: ">=2.7.0 <3.0.0"
- Flutter: ">=1.12.13+hotfix.5"
- Dart sdk: ">=2.12.0-0 <3.0.0"
- 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)
- 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.
- 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
@ -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));
* `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>`
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
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.
- [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.
- [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`.
- [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.
@ -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).
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`.
Also, note that on Android it requires **Android API 20+** (see [AndroidView](https://api.flutter.dev/flutter/widgets/AndroidView-class.html))
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.
Use `InAppWebViewController` to control the WebView instance.
Example:
```dart
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
if (Platform.isAndroid) {
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
}
runApp(new MyApp());
}
@ -258,7 +277,7 @@ class MyApp extends StatefulWidget {
class _MyAppState extends State<MyApp> {
InAppWebViewController webView;
InAppWebViewController? webView;
String url = "";
double progress = 0;
@ -280,81 +299,75 @@ class _MyAppState extends State<MyApp> {
title: const Text('InAppWebView Example'),
),
body: Container(
child: Column(children: <Widget>[
Container(
padding: EdgeInsets.all(20.0),
child: Text(
"CURRENT URL\n${(url.length > 50) ? url.substring(0, 50) + "..." : url}"),
),
Container(
padding: EdgeInsets.all(10.0),
child: progress < 1.0
? LinearProgressIndicator(value: progress)
: Container()),
Expanded(
child: Container(
margin: const EdgeInsets.all(10.0),
decoration:
BoxDecoration(border: Border.all(color: Colors.blueAccent)),
child: InAppWebView(
initialUrl: "https://flutter.dev/",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
debuggingEnabled: true,
)
child: Column(children: <Widget>[
Container(
padding: EdgeInsets.all(20.0),
child: Text(
"CURRENT URL\n${(url.length > 50) ? url.substring(0, 50) + "..." : url}"),
),
Container(
padding: EdgeInsets.all(10.0),
child: progress < 1.0
? LinearProgressIndicator(value: progress)
: Container()),
Expanded(
child: Container(
margin: const EdgeInsets.all(10.0),
decoration:
BoxDecoration(border: Border.all(color: Colors.blueAccent)),
child: InAppWebView(
initialUrl: "https://flutter.dev/",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
)
),
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(
alignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Icon(Icons.arrow_back),
onPressed: () {
if (webView != null) {
webView.goBack();
}
},
),
RaisedButton(
child: Icon(Icons.arrow_forward),
onPressed: () {
if (webView != null) {
webView.goForward();
}
},
),
RaisedButton(
child: Icon(Icons.refresh),
onPressed: () {
if (webView != null) {
webView.reload();
}
},
),
],
),
])),
ButtonBar(
alignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Icon(Icons.arrow_back),
onPressed: () {
webView?.goBack();
},
),
RaisedButton(
child: Icon(Icons.arrow_forward),
onPressed: () {
webView?.goForward();
},
),
RaisedButton(
child: Icon(Icons.refresh),
onPressed: () {
webView?.reload();
},
),
],
),
])),
),
);
}
@ -447,7 +460,7 @@ Methods available:
##### `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.
* `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 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 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
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.
* `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
* `useShouldOverrideUrlLoading`: Set to `true` to be able to listen at the `shouldOverrideUrlLoading` event. The default value is `false`.
* `useOnLoadResource`: Set to `true` to be able to listen at the `onLoadResource` event. 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.
* `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`.
* `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`.
* `applicationNameForUserAgent`: Append to the existing user-agent. Setting userAgent will override this.
* `javaScriptEnabled`: Set to `true` to enable JavaScript. The default value is `true`.
* `debuggingEnabled`: Enables debugging of web contents (HTML / CSS / JavaScript) loaded into any WebViews of this application.
* `cacheEnabled`: Sets whether WebView should use browser caching. The default value is `true`.
* `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`.
* `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`.
* `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`.
* `incognito`: Set to `true` to open a browser window with incognito mode. The default value is `false`.
* `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`.
* `resourceCustomSchemes`: List of custom schemes that the WebView must handle. Use the `onLoadResourceCustomScheme` event to intercept resource requests with custom scheme.
* `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
* `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`.
* `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.
* `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`.
* `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.
* `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"`.
* `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`.
* `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"`.
* `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.
* `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"`.
* `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`.
* `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.
* `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.
* `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`.
* `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"`.
* `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"`.
* `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.
* `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.
* `overScrollMode`: Sets the WebView's over-scroll mode. The default value is `AndroidOverScrollMode.OVER_SCROLL_IF_CONTENT_SCROLLS`.
* `scrollBarStyle`: Specify the style of the scrollbars. The scrollbars can be overlaid or inset. The default value is `AndroidScrollBarStyle.SCROLLBARS_INSIDE_OVERLAY`.
* `textZoom`: Sets the text zoom of the page in percent. The default value is `100`.
* `thirdPartyCookiesEnabled`: Boolean value to enable third party cookies in the WebView.
* `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`.
* `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
* `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`.
* `suppressesIncrementalRendering`: Set to `true` if you want the WebView suppresses content rendering until it is fully loaded into memory. 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`.
* `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`.
* `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.
* `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`.
* `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`.
* `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`.
* `maximumZoomScale`: A floating-point value that specifies the maximum scale factor that can be applied to the scroll view's content. The default value is `1.0`.
* `minimumZoomScale`: A floating-point value that specifies the minimum scale factor that can be applied to the scroll view's content. The default value is `1.0`.
* `contentInsetAdjustmentBehavior`: Configures how safe area insets are added to the adjusted content inset. The default value is `IOSUIScrollViewContentInsetAdjustmentBehavior.NEVER`.
* `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
@ -701,6 +714,9 @@ import 'package:flutter_inappwebview/flutter_inappwebview.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
if (Platform.isAndroid) {
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
}
runApp(new MyApp());
}
@ -711,8 +727,8 @@ class MyApp extends StatefulWidget {
class _MyAppState extends State<MyApp> {
InAppWebViewController webView;
ContextMenu contextMenu;
InAppWebViewController? webView;
ContextMenu? contextMenu;
String url = "";
double progress = 0;
@ -729,7 +745,7 @@ class _MyAppState extends State<MyApp> {
onCreateContextMenu: (hitTestResult) async {
print("onCreateContextMenu");
print(hitTestResult.extra);
print(await webView.getSelectedText());
print(await webView?.getSelectedText());
},
onHideContextMenu: () {
print("onHideContextMenu");
@ -777,23 +793,23 @@ class _MyAppState extends State<MyApp> {
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
debuggingEnabled: true,
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (InAppWebViewController controller, String url) {
onLoadStart: (controller, url) {
setState(() {
this.url = url;
this.url = url ?? '';
});
},
onLoadStop: (InAppWebViewController controller, String url) async {
onLoadStop: (controller, url) async {
setState(() {
this.url = url;
this.url = url ?? '';
});
},
onProgressChanged: (InAppWebViewController controller, int progress) {
onProgressChanged: (controller, progress) {
setState(() {
this.progress = progress / 100;
});
@ -807,25 +823,19 @@ class _MyAppState extends State<MyApp> {
RaisedButton(
child: Icon(Icons.arrow_back),
onPressed: () {
if (webView != null) {
webView.goBack();
}
webView?.goBack();
},
),
RaisedButton(
child: Icon(Icons.arrow_forward),
onPressed: () {
if (webView != null) {
webView.goForward();
}
webView?.goForward();
},
),
RaisedButton(
child: Icon(Icons.refresh),
onPressed: () {
if (webView != null) {
webView.reload();
}
webView?.reload();
},
),
],
@ -858,12 +868,16 @@ As `InAppWebView`, it has the same options and events. Use `InAppWebViewControll
Example:
```dart
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
if (Platform.isAndroid) {
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
}
runApp(new MyApp());
}
@ -874,7 +888,7 @@ class MyApp extends StatefulWidget {
class _MyAppState extends State<MyApp> {
HeadlessInAppWebView headlessWebView;
HeadlessInAppWebView? headlessWebView;
String url = "";
@override
@ -885,7 +899,7 @@ class _MyAppState extends State<MyApp> {
initialUrl: "https://flutter.dev/",
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
debuggingEnabled: true,
),
),
onWebViewCreated: (controller) {
@ -897,19 +911,19 @@ class _MyAppState extends State<MyApp> {
onLoadStart: (controller, url) async {
print("onLoadStart $url");
setState(() {
this.url = url;
this.url = url ?? '';
});
},
onLoadStop: (controller, url) async {
print("onLoadStop $url");
setState(() {
this.url = url;
this.url = url ?? '';
});
},
onUpdateVisitedHistory: (InAppWebViewController controller, String url, bool androidIsReload) {
onUpdateVisitedHistory: (controller, url, androidIsReload) {
print("onUpdateVisitedHistory $url");
setState(() {
this.url = url;
this.url = url ?? '';
});
},
);
@ -918,7 +932,7 @@ class _MyAppState extends State<MyApp> {
@override
void dispose() {
super.dispose();
headlessWebView.dispose();
headlessWebView?.dispose();
}
@override
@ -938,8 +952,8 @@ class _MyAppState extends State<MyApp> {
Center(
child: RaisedButton(
onPressed: () async {
await headlessWebView.dispose();
await headlessWebView.run();
await headlessWebView?.dispose();
await headlessWebView?.run();
},
child: Text("Run HeadlessInAppWebView")),
),
@ -947,7 +961,7 @@ class _MyAppState extends State<MyApp> {
child: RaisedButton(
onPressed: () async {
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) {
print("HeadlessInAppWebView is not running. Click on \"Run HeadlessInAppWebView\"!");
}
@ -957,7 +971,7 @@ class _MyAppState extends State<MyApp> {
Center(
child: RaisedButton(
onPressed: () {
headlessWebView.dispose();
headlessWebView?.dispose();
},
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.
Example:
```dart
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
@ -987,22 +1003,22 @@ class MyInAppBrowser extends InAppBrowser {
}
@override
Future onLoadStart(String url) async {
Future onLoadStart(url) async {
print("\n\nStarted $url\n\n");
}
@override
Future onLoadStop(String url) async {
Future onLoadStop(url) async {
print("\n\nStopped $url\n\n");
}
@override
void onLoadError(String url, int code, String message) {
void onLoadError(url, code, message) {
print("Can't load $url.. Error: $message");
}
@override
void onProgressChanged(int progress) {
void onProgressChanged(progress) {
print("Progress: $progress");
}
@ -1024,7 +1040,7 @@ class MyInAppBrowser extends InAppBrowser {
"ms ---> duration: " +
response.duration.toString() +
"ms " +
response.url);
(response.url ?? ''));
}
@override
@ -1032,13 +1048,16 @@ class MyInAppBrowser extends InAppBrowser {
print("""
console output:
message: ${consoleMessage.message}
messageLevel: ${consoleMessage.messageLevel.toValue()}
messageLevel: ${consoleMessage.messageLevel?.toValue()}
""");
}
}
void main() {
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
if (Platform.isAndroid) {
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
}
runApp(
new MyApp(),
);
@ -1111,27 +1130,27 @@ Specific options of the `InAppBrowser` class are:
##### `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`.
* `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`.
* `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
* `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`.
* `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`.
* `toolbarTopFixedTitle`: Set the action bar's title.
##### `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.
* `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`.
* `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`.
* `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
@ -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.
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.
Create a Class that extends the `ChromeSafariBrowser` Class in order to override the callbacks to manage the browser events. Example:
```dart
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
class MyInAppBrowser extends InAppBrowser {
@override
Future onLoadStart(String url) async {
Future onLoadStart(url) async {
print("\n\nStarted $url\n\n");
}
@override
Future onLoadStop(String url) async {
Future onLoadStop(url) async {
print("\n\nStopped $url\n\n");
}
@override
void onLoadError(String url, int code, String message) {
void onLoadError(url, code, message) {
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();
runApp(
new MyApp(),
);
if (Platform.isAndroid) {
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
}
runApp(new MyApp());
}
class MyApp extends StatefulWidget {
@ -1262,11 +1286,11 @@ Screenshots:
#### `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.
* `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`.
#### `ChromeSafariBrowser` options
@ -1274,18 +1298,18 @@ Screenshots:
##### `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`.
* `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`.
* `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`.
* `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
* `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`.
* `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.
* `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`.
@ -1310,6 +1334,9 @@ InAppLocalhostServer localhostServer = new InAppLocalhostServer();
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
await localhostServer.start();
if (Platform.isAndroid) {
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
}
runApp(new MyApp());
}
@ -1330,17 +1357,17 @@ Future main() async {
initialUrl: "http://localhost:8080/assets/index.html",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
inAppWebViewOptions: InAppWebViewOptions(
debuggingEnabled: true,
crossPlatform: InAppWebViewOptions(
)
),
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();
}
if (!trigger.loadType.isEmpty()) {
URI cUrl = new URI(webViewUrl[0]);
String cHost = cUrl.getHost();
int cPort = cUrl.getPort();
String cScheme = cUrl.getScheme();
if (webViewUrl[0] != null) {
if (!trigger.loadType.isEmpty()) {
URI cUrl = new URI(webViewUrl[0]);
String cHost = cUrl.getHost();
int cPort = cUrl.getPort();
String cScheme = cUrl.getScheme();
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)) )
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))
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)) )
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) {

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");
}
// MutableContextWrapper mMutableContext = new MutableContextWrapper(Shared.activity);
// 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);
webView = new InAppWebView(context, this, id, windowId, options, contextMenu, containerView);
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();
if (windowId != null) {

View File

@ -677,9 +677,6 @@ final public class InAppWebView extends InputAwareWebView {
WebSettings settings = getSettings();
settings.setJavaScriptEnabled(options.javaScriptEnabled);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
setWebContentsDebuggingEnabled(options.debuggingEnabled);
}
settings.setJavaScriptCanOpenWindowsAutomatically(options.javaScriptCanOpenWindowsAutomatically);
settings.setBuiltInZoomControls(options.builtInZoomControls);
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() {
@Override
public void run() {
@ -1133,9 +1130,6 @@ final public class InAppWebView extends InputAwareWebView {
if (newOptionsMap.get("javaScriptEnabled") != null && options.javaScriptEnabled != 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) {
String placeholderValue = newOptions.useShouldInterceptAjaxRequest ? "true" : "false";
String sourceJs = InAppWebView.enableVariableForShouldInterceptAjaxRequestJS.replace("$PLACEHOLDER_VALUE", placeholderValue);
@ -1505,14 +1499,11 @@ final public class InAppWebView extends InputAwareWebView {
}
@Override
protected void onScrollChanged (int l,
int t,
int oldl,
int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
int x = (int) (l/scale);
int y = (int) (t/scale);
protected void onScrollChanged (int x,
int y,
int oldX,
int oldY) {
super.onScrollChanged(x, y, oldX, oldY);
if (floatingContextMenu != null) {
floatingContextMenu.setAlpha(0f);
@ -1662,12 +1653,18 @@ final public class InAppWebView extends InputAwareWebView {
@Override
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);
}
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
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);
}
@ -1711,6 +1708,7 @@ final public class InAppWebView extends InputAwareWebView {
final MenuItem menuItem = actionMenu.getItem(i);
final int itemId = menuItem.getItemId();
final String itemTitle = menuItem.getTitle().toString();
TextView text = (TextView) LayoutInflater.from(this.getContext())
.inflate(R.layout.floating_action_mode_item, this, false);
text.setText(itemTitle);
@ -1855,7 +1853,7 @@ final public class InAppWebView extends InputAwareWebView {
" var clientRect = range.getClientRects();" +
" if (clientRect.length > 0) {" +
" rangeY = clientRect[0].y;" +
" } else if (document.activeElement) {" +
" } else if (document.activeElement != null && document.activeElement.tagName.toLowerCase() !== 'iframe') {" +
" var boundingClientRect = document.activeElement.getBoundingClientRect();" +
" rangeY = boundingClientRect.y;" +
" }" +
@ -1865,7 +1863,7 @@ final public class InAppWebView extends InputAwareWebView {
@Override
public void onReceiveValue(String value) {
if (floatingContextMenu != null) {
if (value != null) {
if (value != null && !value.equals("null")) {
int x = contextMenuPoint.x;
int y = (int) ((Float.parseFloat(value) * scale) + (floatingContextMenu.getHeight() / 3.5));
contextMenuPoint.y = y;
@ -1873,6 +1871,7 @@ final public class InAppWebView extends InputAwareWebView {
} else {
floatingContextMenu.setVisibility(View.VISIBLE);
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.annotation.TargetApi;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.AssetFileDescriptor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
@ -46,6 +48,7 @@ import com.pichillilorenzo.flutter_inappwebview.Shared;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
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_LEGACY = 3;
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(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER);
@ -810,39 +814,37 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
// this filename instead
switch (requestCode) {
case PICKER:
if (resultCode != RESULT_OK) {
if (InAppWebViewFlutterPlugin.filePathCallback != null) {
InAppWebViewFlutterPlugin.filePathCallback.onReceiveValue(null);
}
} else {
Uri result[] = this.getSelectedFiles(data, resultCode);
if (result != null) {
InAppWebViewFlutterPlugin.filePathCallback.onReceiveValue(result);
} else {
InAppWebViewFlutterPlugin.filePathCallback.onReceiveValue(new Uri[]{outputFileUri});
}
Uri[] results = null;
if (resultCode == RESULT_OK) {
results = getSelectedFiles(data, resultCode);
}
if (InAppWebViewFlutterPlugin.filePathCallback != null) {
InAppWebViewFlutterPlugin.filePathCallback.onReceiveValue(results);
}
break;
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);
break;
}
InAppWebViewFlutterPlugin.filePathCallback = null;
InAppWebViewFlutterPlugin.filePathCallbackLegacy = null;
outputFileUri = null;
imageOutputFileUri = null;
videoOutputFileUri = null;
return true;
}
private Uri[] getSelectedFiles(Intent data, int resultCode) {
if (data == null) {
return null;
}
// 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) {
return WebChromeClient.FileChooserParams.parseResult(resultCode, data);
} else {
@ -851,7 +853,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
}
// we have multiple files selected
if (data.getClipData() != null) {
if (data != null && data.getClipData() != null) {
final int numSelectedFiles = data.getClipData().getItemCount();
Uri[] result = new Uri[numSelectedFiles];
for (int i = 0; i < numSelectedFiles; i++) {
@ -859,6 +861,40 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
}
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;
}
@ -935,15 +971,15 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
private Intent getPhotoIntent() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
outputFileUri = getOutputUri(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
imageOutputFileUri = getOutputUri(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageOutputFileUri);
return intent;
}
private Intent getVideoIntent() {
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
outputFileUri = getOutputUri(MediaStore.ACTION_VIDEO_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
videoOutputFileUri = getOutputUri(MediaStore.ACTION_VIDEO_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, videoOutputFileUri);
return intent;
}
@ -971,6 +1007,20 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
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) {
String mimeType = types;
if (types.matches("\\.\\w+")) {
@ -981,7 +1031,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
private Boolean acceptsImages(String[] types) {
String[] mimeTypes = getAcceptedMimeType(types);
return isArrayEmpty(mimeTypes) || arrayContainsString(mimeTypes, "image");
return acceptsAny(types) || arrayContainsString(mimeTypes, "image");
}
private Boolean acceptsVideo(String types) {
@ -994,7 +1044,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
private Boolean acceptsVideo(String[] types) {
String[] mimeTypes = getAcceptedMimeType(types);
return isArrayEmpty(mimeTypes) || arrayContainsString(mimeTypes, "video");
return acceptsAny(types) || arrayContainsString(mimeTypes, "video");
}
private Boolean arrayContainsString(String[] array, String pattern) {
@ -1056,31 +1106,29 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
String prefix = "";
String suffix = "";
String dir = "";
String filename = "";
if (intentType.equals(MediaStore.ACTION_IMAGE_CAPTURE)) {
prefix = "image-";
prefix = "image";
suffix = ".jpg";
dir = Environment.DIRECTORY_PICTURES;
} else if (intentType.equals(MediaStore.ACTION_VIDEO_CAPTURE)) {
prefix = "video-";
prefix = "video";
suffix = ".mp4";
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
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
// only this Directory works on all tested Android versions
// ctx.getExternalFilesDir(dir) was failing on Android 5.0 (sdk 21)
File storageDir = Environment.getExternalStoragePublicDirectory(dir);
String filename = String.format("%s-%d%s", prefix, System.currentTimeMillis(), suffix);
return new File(storageDir, filename);
}
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
File storageDir = activity.getApplicationContext().getExternalFilesDir(null);
return File.createTempFile(filename, suffix, storageDir);
return File.createTempFile(prefix, suffix, storageDir);
}
private Boolean isArrayEmpty(String[] arr) {

View File

@ -176,7 +176,9 @@ public class InAppWebViewClient extends WebViewClient {
if (webView.options.useOnLoadResource) {
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.onWindowBlurEventJS.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 applicationNameForUserAgent = "";
public Boolean javaScriptEnabled = true;
public Boolean debuggingEnabled = false;
public Boolean javaScriptCanOpenWindowsAutomatically = false;
public Boolean mediaPlaybackRequiresUserGesture = true;
public Integer minimumFontSize = 8;
@ -44,6 +43,8 @@ public class InAppWebViewOptions implements Options<InAppWebView> {
public Boolean disableHorizontalScroll = false;
public Boolean disableContextMenu = false;
public Boolean supportZoom = true;
public Boolean allowFileAccessFromFileURLs = false;
public Boolean allowUniversalAccessFromFileURLs = false;
public Integer textZoom = 100;
public Boolean clearSessionCache = false;
@ -56,8 +57,6 @@ public class InAppWebViewOptions implements Options<InAppWebView> {
public Integer mixedContentMode;
public Boolean allowContentAccess = true;
public Boolean allowFileAccess = true;
public Boolean allowFileAccessFromFileURLs = true;
public Boolean allowUniversalAccessFromFileURLs = true;
public String appCachePath;
public Boolean blockNetworkImage = false;
public Boolean blockNetworkLoads = false;
@ -97,6 +96,7 @@ public class InAppWebViewOptions implements Options<InAppWebView> {
public Boolean useShouldInterceptRequest = false;
public Boolean useOnRenderProcessGone = false;
public Boolean disableDefaultErrorPage = false;
public Boolean useHybridComposition = false;
@Override
public InAppWebViewOptions parse(Map<String, Object> options) {
@ -129,9 +129,6 @@ public class InAppWebViewOptions implements Options<InAppWebView> {
case "javaScriptEnabled":
javaScriptEnabled = (Boolean) value;
break;
case "debuggingEnabled":
debuggingEnabled = (Boolean) value;
break;
case "javaScriptCanOpenWindowsAutomatically":
javaScriptCanOpenWindowsAutomatically = (Boolean) value;
break;
@ -339,6 +336,9 @@ public class InAppWebViewOptions implements Options<InAppWebView> {
case "disableDefaultErrorPage":
disableDefaultErrorPage = (Boolean) value;
break;
case "useHybridComposition":
useHybridComposition = (Boolean) value;
break;
}
}
@ -355,7 +355,6 @@ public class InAppWebViewOptions implements Options<InAppWebView> {
options.put("userAgent", userAgent);
options.put("applicationNameForUserAgent", applicationNameForUserAgent);
options.put("javaScriptEnabled", javaScriptEnabled);
options.put("debuggingEnabled", debuggingEnabled);
options.put("javaScriptCanOpenWindowsAutomatically", javaScriptCanOpenWindowsAutomatically);
options.put("mediaPlaybackRequiresUserGesture", mediaPlaybackRequiresUserGesture);
options.put("minimumFontSize", minimumFontSize);
@ -425,6 +424,7 @@ public class InAppWebViewOptions implements Options<InAppWebView> {
options.put("useShouldInterceptRequest", useShouldInterceptRequest);
options.put("useOnRenderProcessGone", useOnRenderProcessGone);
options.put("disableDefaultErrorPage", disableDefaultErrorPage);
options.put("useHybridComposition", useHybridComposition);
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
* 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
*/
public class InputAwareWebView extends WebView {
@ -234,9 +236,9 @@ public class InputAwareWebView extends WebView {
private boolean isCalledFromListPopupWindowShow() {
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
for (int i = 0; i < stackTraceElements.length; i++) {
if (stackTraceElements[i].getClassName().equals(ListPopupWindow.class.getCanonicalName())
&& stackTraceElements[i].getMethodName().equals("show")) {
for (StackTraceElement stackTraceElement : stackTraceElements) {
if (stackTraceElement.getClassName().equals(ListPopupWindow.class.getCanonicalName())
&& stackTraceElement.getMethodName().equals("show")) {
return true;
}
}

View File

@ -72,6 +72,13 @@ public class InAppWebViewStatic implements MethodChannel.MethodCallHandler {
result.success(null);
}
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:
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;
import android.os.Bundle;
import dev.flutter.plugins.e2e.E2EPlugin;
import io.flutter.app.FlutterActivity;
import dev.flutter.plugins.integration_test.IntegrationTestPlugin;
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
public class EmbedderV1Activity extends FlutterActivity {
@SuppressWarnings("deprecation")
public class EmbedderV1Activity extends io.flutter.app.FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
E2EPlugin.registerWith(registrarFor("dev.flutter.plugins.e2e.E2EPlugin"));
IntegrationTestPlugin.registerWith(
registrarFor("dev.flutter.plugins.integration_test.IntegrationTestPlugin"));
InAppWebViewFlutterPlugin.registerWith(
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 "Generated.xcconfig"
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 "Generated.xcconfig"

View File

@ -2,14 +2,12 @@
# This is a generated file; do not edit or check into version control.
export "FLUTTER_ROOT=/Users/lorenzopichilli/flutter"
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 "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_NUMBER=1"
export "DART_OBFUSCATION=false"
export "TRACK_WIDGET_CREATION=true"
export "TRACK_WIDGET_CREATION=false"
export "TREE_SHAKE_ICONS=false"
export "PACKAGE_CONFIG=.packages"

View File

@ -254,19 +254,17 @@
);
inputPaths = (
"${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_inappwebview/flutter_inappwebview.framework",
"${BUILT_PRODUCTS_DIR}/integration_test/integration_test.framework",
"${BUILT_PRODUCTS_DIR}/path_provider/path_provider.framework",
"${BUILT_PRODUCTS_DIR}/url_launcher/url_launcher.framework",
);
name = "[CP] Embed Pods Frameworks";
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_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}/url_launcher.framework",
);
@ -451,7 +449,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = com.pichillilorenzo.flutter_inappwebviewExample;
PRODUCT_BUNDLE_IDENTIFIER = "flutter-inappwebviewExample";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";

View File

@ -2,6 +2,6 @@
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
location = "self:">
</FileRef>
</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">
<plist version="1.0">
<dict>
<key>NSLocationAlwaysUsageDescription</key>
<string>Need location</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>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<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>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsLocalNetworking</key>
<true/>
</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>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</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>
<array>
<string>UIInterfaceOrientationPortrait</string>
@ -57,7 +55,13 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>NSBonjourServices</key>
<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>
</plist>

View File

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

View File

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

View File

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

View File

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

View File

@ -10,8 +10,8 @@ description: Demonstrates how to use the flutter_inappwebview plugin.
version: 1.0.0+1
environment:
sdk: ">=2.0.0-dev.68.0 <3.0.0"
flutter: ">=1.10.0 <2.0.0"
sdk: ">=2.12.0-0 <3.0.0"
flutter: ">=1.22.0"
dependencies:
flutter:
@ -19,20 +19,26 @@ dependencies:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2
flutter_downloader: ^1.4.4
path_provider: ^1.6.9
permission_handler: ^5.0.0+hotfix.6
url_launcher: ^5.4.11
cupertino_icons: ^1.0.2
flutter_downloader: ^1.5.2
path_provider: ^1.6.27
permission_handler: ^5.0.1+1
url_launcher: ^6.0.0-nullsafety.4
# connectivity: ^0.4.5+6
flutter_inappwebview:
path: ../
dev_dependencies:
e2e: "^0.2.0"
flutter_test:
sdk: flutter
flutter_driver:
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
# following page: https://www.dartlang.org/tools/pub/pubspec
@ -53,6 +59,8 @@ flutter:
- assets/css/
- assets/images/
- assets/favicon.ico
- assets/sample_audio.ogg
- assets/sample_video.mp4
- test_assets/certificate.pfx
- test_assets/in_app_webview_initial_file_test.html
- test_assets/in_app_webview_on_load_resource_test.html
@ -64,6 +72,8 @@ flutter:
- test_assets/css/
- test_assets/images/
- 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:
# 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/.pub" />
<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/.pub" />
<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/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/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$/flutter_inappbrowser_tests/.dart_tool" />
<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 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.parse(options: options)

View File

@ -1153,7 +1153,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
scrollView.maximumZoomScale = CGFloat(options.maximumZoomScale)
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.
if options.clearCache {
@ -1352,6 +1352,14 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
configuration.suppressesIncrementalRendering = options.suppressesIncrementalRendering
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 options.incognito {
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 {
clearCache()
}
@ -2025,7 +2041,9 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic ||
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 prot = challenge.protectionSpace.protocol
let realm = challenge.protectionSpace.realm
@ -2498,6 +2516,9 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
}
if !handledByClient, InAppWebView.windowWebViews[windowId] != nil {
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 applicationNameForUserAgent = ""
var javaScriptEnabled = true
var debuggingEnabled = true
var javaScriptCanOpenWindowsAutomatically = false
var mediaPlaybackRequiresUserGesture = true
var verticalScrollBarEnabled = true
@ -35,6 +34,8 @@ public class InAppWebViewOptions: Options<InAppWebView> {
var disableHorizontalScroll = false
var disableContextMenu = false
var supportZoom = true
var allowUniversalAccessFromFileURLs = false
var allowFileAccessFromFileURLs = false
var disallowOverScroll = false
var enableViewportScale = false
@ -106,6 +107,8 @@ public class InAppWebViewOptions: Options<InAppWebView> {
realOptions["isPagingEnabled"] = webView.scrollView.isPagingEnabled
realOptions["maximumZoomScale"] = webView.scrollView.maximumZoomScale
realOptions["minimumZoomScale"] = webView.scrollView.minimumZoomScale
realOptions["allowUniversalAccessFromFileURLs"] = configuration.value(forKey: "allowUniversalAccessFromFileURLs")
realOptions["allowFileAccessFromFileURLs"] = configuration.preferences.value(forKey: "allowFileAccessFromFileURLs")
}
return realOptions
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,6 @@
import 'dart:async';
import 'dart:collection';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.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
class ChromeSafariBrowser {
String uuid;
InAppBrowser browserFallback;
late String uuid;
InAppBrowser? browserFallback;
Map<int, ChromeSafariBrowserMenuItem> _menuItems = new HashMap();
bool _isOpened = false;
MethodChannel _channel;
late MethodChannel _channel;
static const MethodChannel _sharedChannel =
const MethodChannel('com.pichillilorenzo/flutter_chromesafaribrowser');
@ -48,7 +47,9 @@ class ChromeSafariBrowser {
String url = call.arguments["url"];
String title = call.arguments["title"];
int id = call.arguments["id"].toInt();
this._menuItems[id].action(url, title);
if (this._menuItems[id] != null) {
this._menuItems[id]!.action(url, title);
}
break;
default:
throw UnimplementedError("Unimplemented ${call.method} method");
@ -67,14 +68,14 @@ class ChromeSafariBrowser {
///
///[contextMenuFallback]: Context Menu used by the [InAppBrowser] instance fallback.
Future<void> open(
{@required String url,
ChromeSafariBrowserClassOptions options,
Map<String, String> headersFallback = const {},
InAppBrowserClassOptions optionsFallback}) async {
assert(url != null && url.isNotEmpty);
{required String url,
ChromeSafariBrowserClassOptions? options,
Map<String, String>? headersFallback = const {},
InAppBrowserClassOptions? optionsFallback}) async {
assert(url.isNotEmpty);
this.throwIsAlreadyOpened(message: 'Cannot open $url!');
List<Map<String, dynamic>> menuItemList = new List();
List<Map<String, dynamic>> menuItemList = [];
_menuItems.forEach((key, value) {
menuItemList.add({"id": value.id, "label": value.label});
});
@ -162,7 +163,7 @@ class ChromeSafariBrowserMenuItem {
final void Function(String url, String title) action;
ChromeSafariBrowserMenuItem(
{@required this.id, @required this.label, @required this.action});
{required this.id, required this.label, required this.action});
Map<String, dynamic> toMap() {
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.
ContentBlockerAction action;
ContentBlocker({@required this.trigger, @required this.action});
ContentBlocker({required this.trigger, required this.action});
Map<String, Map<String, dynamic>> toMap() {
return {"trigger": trigger.toMap(), "action": action.toMap()};
@ -24,9 +24,9 @@ class ContentBlocker {
static ContentBlocker fromMap(Map<dynamic, Map<dynamic, dynamic>> map) {
return ContentBlocker(
trigger: ContentBlockerTrigger.fromMap(
Map<String, dynamic>.from(map["trigger"])),
Map<String, dynamic>.from(map["trigger"]!)),
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.
class ContentBlockerTrigger {
///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.
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.
///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.
///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.
///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.
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.
///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.
///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(
{@required String urlFilter,
{required String urlFilter,
bool urlFilterIsCaseSensitive = false,
List<ContentBlockerTriggerResourceType> resourceType = const [],
List<String> ifDomain = const [],
@ -74,7 +74,6 @@ class ContentBlockerTrigger {
List<String> ifTopUrl = const [],
List<String> unlessTopUrl = const []}) {
this.urlFilter = urlFilter;
assert(this.urlFilter != null);
this.resourceType = resourceType;
this.urlFilterIsCaseSensitive = urlFilterIsCaseSensitive;
this.ifDomain = ifDomain;
@ -124,13 +123,19 @@ class ContentBlockerTrigger {
List<String> resourceTypeStringList =
List<String>.from(map["resource-type"] ?? []);
resourceTypeStringList.forEach((type) {
resourceType.add(ContentBlockerTriggerResourceType.fromValue(type));
resourceTypeStringList.forEach((typeValue) {
var type = ContentBlockerTriggerResourceType.fromValue(typeValue);
if (type != null) {
resourceType.add(type);
}
});
List<String> loadTypeStringList = List<String>.from(map["load-type"] ?? []);
loadTypeStringList.forEach((type) {
loadType.add(ContentBlockerTriggerLoadType.fromValue(type));
loadTypeStringList.forEach((typeValue) {
var type = ContentBlockerTriggerLoadType.fromValue(typeValue);
if (type != null) {
loadType.add(type);
}
});
return ContentBlockerTrigger(
@ -152,16 +157,15 @@ class ContentBlockerTrigger {
///Group the rules with similar actions together to improve performance.
class ContentBlockerAction {
///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.
///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(
{@required ContentBlockerActionType type, String selector}) {
{required ContentBlockerActionType type, String? selector}) {
this.type = type;
assert(this.type != null);
if (this.type == ContentBlockerActionType.CSS_DISPLAY_NONE) {
assert(selector != null);
}
@ -183,7 +187,7 @@ class ContentBlockerAction {
static ContentBlockerAction fromMap(Map<String, dynamic> map) {
return ContentBlockerAction(
type: ContentBlockerActionType.fromValue(map["type"]),
type: ContentBlockerActionType.fromValue(map["type"])!,
selector: map["selector"]);
}
}

View File

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

View File

@ -1,6 +1,5 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'types.dart';
@ -11,19 +10,19 @@ import 'types.dart';
///
///**NOTE for iOS**: available from iOS 11.0+.
class CookieManager {
static CookieManager _instance;
static CookieManager? _instance;
static const MethodChannel _channel = const MethodChannel(
'com.pichillilorenzo/flutter_inappwebview_cookiemanager');
///Gets the cookie manager shared instance.
static CookieManager instance() {
return (_instance != null) ? _instance : _init();
return (_instance != null) ? _instance! : _init();
}
static CookieManager _init() {
_channel.setMethodCallHandler(_handleMethod);
_instance = CookieManager();
return _instance;
return _instance!;
}
static Future<dynamic> _handleMethod(MethodCall call) async {}
@ -33,23 +32,23 @@ class CookieManager {
///The default value of [path] is `"/"`.
///If [domain] is `null`, its default value will be the domain name of [url].
Future<void> setCookie(
{@required String url,
@required String name,
@required String value,
String domain,
{required String url,
required String name,
required String value,
String? domain,
String path = "/",
int expiresDate,
int maxAge,
bool isSecure,
bool isHttpOnly,
HTTPCookieSameSitePolicy sameSite}) async {
int? expiresDate,
int? maxAge,
bool? isSecure,
bool? isHttpOnly,
HTTPCookieSameSitePolicy? sameSite}) async {
if (domain == null) domain = _getDomainName(url);
assert(url != null && url.isNotEmpty);
assert(name != null && name.isNotEmpty);
assert(value != null && value.isNotEmpty);
assert(domain != null && domain.isNotEmpty);
assert(path != null && path.isNotEmpty);
assert(url.isNotEmpty);
assert(name.isNotEmpty);
assert(value.isNotEmpty);
assert(domain.isNotEmpty);
assert(path.isNotEmpty);
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url);
@ -67,8 +66,8 @@ class CookieManager {
}
///Gets all the cookies for the given [url].
Future<List<Cookie>> getCookies({@required String url}) async {
assert(url != null && url.isNotEmpty);
Future<List<Cookie>> getCookies({required String url}) async {
assert(url.isNotEmpty);
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url);
@ -94,10 +93,10 @@ class CookieManager {
}
///Gets a cookie by its [name] for the given [url].
Future<Cookie> getCookie(
{@required String url, @required String name}) async {
assert(url != null && url.isNotEmpty);
assert(name != null && name.isNotEmpty);
Future<Cookie?> getCookie(
{required String url, required String name}) async {
assert(url.isNotEmpty);
assert(name.isNotEmpty);
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url);
@ -126,16 +125,16 @@ class CookieManager {
///The default value of [path] is `"/"`.
///If [domain] is `null` or empty, its default value will be the domain name of [url].
Future<void> deleteCookie(
{@required String url,
@required String name,
{required String url,
required String name,
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(name != null && name.isNotEmpty);
assert(domain != null && url.isNotEmpty);
assert(path != null && url.isNotEmpty);
assert(url.isNotEmpty);
assert(name.isNotEmpty);
assert(url.isNotEmpty);
assert(url.isNotEmpty);
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url);
@ -150,12 +149,12 @@ class CookieManager {
///The default value of [path] is `"/"`.
///If [domain] is `null` or empty, its default value will be the domain name of [url].
Future<void> deleteCookies(
{@required String url, String domain = "", String path = "/"}) async {
if (domain == null || domain.isEmpty) domain = _getDomainName(url);
{required String url, String domain = "", String path = "/"}) async {
if (domain.isEmpty) domain = _getDomainName(url);
assert(url != null && url.isNotEmpty);
assert(domain != null && url.isNotEmpty);
assert(path != null && url.isNotEmpty);
assert(url.isNotEmpty);
assert(url.isNotEmpty);
assert(url.isNotEmpty);
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url);
@ -173,6 +172,7 @@ class CookieManager {
String _getDomainName(String url) {
Uri uri = Uri.parse(url);
String domain = uri.host;
// ignore: unnecessary_null_comparison
if (domain == null) return "";
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.
class HeadlessInAppWebView implements WebView {
String uuid;
late String uuid;
bool _isDisposed = true;
static const MethodChannel _sharedChannel =
const MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview');
///WebView Controller that can be used to access the [InAppWebViewController] API.
InAppWebViewController webViewController;
late InAppWebViewController webViewController;
///The window id of a [CreateWindowRequest.windowId].
final int windowId;
final int? windowId;
HeadlessInAppWebView(
{this.windowId,
@ -89,7 +89,9 @@ class HeadlessInAppWebView implements WebView {
Future<dynamic> handleMethod(MethodCall call) async {
switch (call.method) {
case "onHeadlessWebViewCreated":
onWebViewCreated(webViewController);
if (onWebViewCreated != null) {
onWebViewCreated!(webViewController);
}
break;
default:
return webViewController.handleMethod(call);
@ -107,12 +109,13 @@ class HeadlessInAppWebView implements WebView {
args.putIfAbsent(
'params',
() => <String, dynamic>{
'initialUrl': '${Uri.parse(this.initialUrl)}',
'initialUrl': this.initialUrl != null ? '${Uri.parse(this.initialUrl!)}' : '',
'initialFile': this.initialFile,
'initialData': this.initialData?.toMap(),
'initialHeaders': this.initialHeaders,
'initialOptions': this.initialOptions?.toMap() ?? {},
'contextMenu': this.contextMenu?.toMap() ?? {}
'contextMenu': this.contextMenu?.toMap() ?? {},
'windowId': this.windowId
});
await _sharedChannel.invokeMethod('createHeadlessWebView', args);
}
@ -129,242 +132,242 @@ class HeadlessInAppWebView implements WebView {
}
@override
final void Function(InAppWebViewController controller)
final void Function(InAppWebViewController controller)?
androidOnGeolocationPermissionsHidePrompt;
@override
final Future<GeolocationPermissionShowPromptResponse> Function(
InAppWebViewController controller, String origin)
final Future<GeolocationPermissionShowPromptResponse?> Function(
InAppWebViewController controller, String origin)?
androidOnGeolocationPermissionsShowPrompt;
@override
final Future<PermissionRequestResponse> Function(
final Future<PermissionRequestResponse?> Function(
InAppWebViewController controller,
String origin,
List<String> resources) androidOnPermissionRequest;
List<String> resources)? androidOnPermissionRequest;
@override
final Future<SafeBrowsingResponse> Function(InAppWebViewController controller,
String url, SafeBrowsingThreat threatType) androidOnSafeBrowsingHit;
final Future<SafeBrowsingResponse?> Function(InAppWebViewController controller,
String url, SafeBrowsingThreat? threatType)? androidOnSafeBrowsingHit;
@override
final InAppWebViewInitialData initialData;
final InAppWebViewInitialData? initialData;
@override
final String initialFile;
final String? initialFile;
@override
final Map<String, String> initialHeaders;
final Map<String, String>? initialHeaders;
@override
final InAppWebViewGroupOptions initialOptions;
final InAppWebViewGroupOptions? initialOptions;
@override
final ContextMenu contextMenu;
final ContextMenu? contextMenu;
@override
final String initialUrl;
final String? initialUrl;
@override
final void Function(InAppWebViewController controller, String url)
final void Function(InAppWebViewController controller, String? url)?
onPageCommitVisible;
@override
final void Function(InAppWebViewController controller, String title)
final void Function(InAppWebViewController controller, String? title)?
onTitleChanged;
@override
final void Function(InAppWebViewController controller)
final void Function(InAppWebViewController controller)?
iosOnDidReceiveServerRedirectForProvisionalNavigation;
@override
final void Function(InAppWebViewController controller)
final void Function(InAppWebViewController controller)?
iosOnWebContentProcessDidTerminate;
@override
final Future<AjaxRequestAction> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest)
InAppWebViewController controller, AjaxRequest ajaxRequest)?
onAjaxProgress;
@override
final Future<AjaxRequestAction> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest)
final Future<AjaxRequestAction?> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest)?
onAjaxReadyStateChange;
@override
final void Function(
InAppWebViewController controller, ConsoleMessage consoleMessage)
InAppWebViewController controller, ConsoleMessage consoleMessage)?
onConsoleMessage;
@override
final Future<bool> Function(InAppWebViewController controller,
CreateWindowRequest createWindowRequest) onCreateWindow;
final Future<bool?> Function(InAppWebViewController controller,
CreateWindowRequest createWindowRequest)? onCreateWindow;
@override
final void Function(InAppWebViewController controller) onCloseWindow;
final void Function(InAppWebViewController controller)? onCloseWindow;
@override
final void Function(InAppWebViewController controller) onWindowFocus;
final void Function(InAppWebViewController controller)? onWindowFocus;
@override
final void Function(InAppWebViewController controller) onWindowBlur;
final void Function(InAppWebViewController controller)? onWindowBlur;
@override
final void Function(InAppWebViewController controller) androidOnRequestFocus;
final void Function(InAppWebViewController controller)? androidOnRequestFocus;
@override
final void Function(InAppWebViewController controller, String url)
final void Function(InAppWebViewController controller, String url)?
onDownloadStart;
@override
final void Function(InAppWebViewController controller, int activeMatchOrdinal,
int numberOfMatches, bool isDoneCounting) onFindResultReceived;
int numberOfMatches, bool isDoneCounting)? onFindResultReceived;
@override
final Future<JsAlertResponse> Function(
InAppWebViewController controller, JsAlertRequest jsAlertRequest)
final Future<JsAlertResponse?> Function(
InAppWebViewController controller, JsAlertRequest jsAlertRequest)?
onJsAlert;
@override
final Future<JsConfirmResponse> Function(
InAppWebViewController controller, JsConfirmRequest jsConfirmRequest)
final Future<JsConfirmResponse?> Function(
InAppWebViewController controller, JsConfirmRequest jsConfirmRequest)?
onJsConfirm;
@override
final Future<JsPromptResponse> Function(
InAppWebViewController controller, JsPromptRequest jsPromptRequest)
final Future<JsPromptResponse?> Function(
InAppWebViewController controller, JsPromptRequest jsPromptRequest)?
onJsPrompt;
@override
final void Function(InAppWebViewController controller, String url, int code,
String message) onLoadError;
final void Function(InAppWebViewController controller, String? url, int code,
String message)? onLoadError;
@override
final void Function(InAppWebViewController controller, String url,
int statusCode, String description) onLoadHttpError;
final void Function(InAppWebViewController controller, String? url,
int statusCode, String description)? onLoadHttpError;
@override
final void Function(
InAppWebViewController controller, LoadedResource resource)
InAppWebViewController controller, LoadedResource resource)?
onLoadResource;
@override
final Future<CustomSchemeResponse> Function(
InAppWebViewController controller, String scheme, String url)
final Future<CustomSchemeResponse?> Function(
InAppWebViewController controller, String scheme, String url)?
onLoadResourceCustomScheme;
@override
final void Function(InAppWebViewController controller, String url)
final void Function(InAppWebViewController controller, String? url)?
onLoadStart;
@override
final void Function(InAppWebViewController controller, String url) onLoadStop;
final void Function(InAppWebViewController controller, String? url)? onLoadStop;
@override
final void Function(InAppWebViewController controller,
InAppWebViewHitTestResult hitTestResult) onLongPressHitTestResult;
InAppWebViewHitTestResult hitTestResult)? onLongPressHitTestResult;
@override
final void Function(InAppWebViewController controller, String url) onPrint;
final void Function(InAppWebViewController controller, String? url)? onPrint;
@override
final void Function(InAppWebViewController controller, int progress)
final void Function(InAppWebViewController controller, int progress)?
onProgressChanged;
@override
final Future<ClientCertResponse> Function(
InAppWebViewController controller, ClientCertChallenge challenge)
final Future<ClientCertResponse?> Function(
InAppWebViewController controller, ClientCertChallenge challenge)?
onReceivedClientCertRequest;
@override
final Future<HttpAuthResponse> Function(
InAppWebViewController controller, HttpAuthChallenge challenge)
final Future<HttpAuthResponse?> Function(
InAppWebViewController controller, HttpAuthChallenge challenge)?
onReceivedHttpAuthRequest;
@override
final Future<ServerTrustAuthResponse> Function(
InAppWebViewController controller, ServerTrustChallenge challenge)
final Future<ServerTrustAuthResponse?> Function(
InAppWebViewController controller, ServerTrustChallenge challenge)?
onReceivedServerTrustAuthRequest;
@override
final void Function(InAppWebViewController controller, int x, int y)
final void Function(InAppWebViewController controller, int x, int y)?
onScrollChanged;
@override
final void Function(
InAppWebViewController controller, String url, bool androidIsReload)
InAppWebViewController controller, String? url, bool? androidIsReload)?
onUpdateVisitedHistory;
@override
final void Function(InAppWebViewController controller) onWebViewCreated;
final void Function(InAppWebViewController controller)? onWebViewCreated;
@override
final Future<AjaxRequest> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest)
final Future<AjaxRequest?> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest)?
shouldInterceptAjaxRequest;
@override
final Future<FetchRequest> Function(
InAppWebViewController controller, FetchRequest fetchRequest)
final Future<FetchRequest?> Function(
InAppWebViewController controller, FetchRequest fetchRequest)?
shouldInterceptFetchRequest;
@override
final Future<ShouldOverrideUrlLoadingAction> Function(
final Future<ShouldOverrideUrlLoadingAction?> Function(
InAppWebViewController controller,
ShouldOverrideUrlLoadingRequest shouldOverrideUrlLoadingRequest)
ShouldOverrideUrlLoadingRequest shouldOverrideUrlLoadingRequest)?
shouldOverrideUrlLoading;
@override
final void Function(InAppWebViewController controller) onEnterFullscreen;
final void Function(InAppWebViewController controller)? onEnterFullscreen;
@override
final void Function(InAppWebViewController controller) onExitFullscreen;
final void Function(InAppWebViewController controller)? onExitFullscreen;
@override
final Future<WebResourceResponse> Function(
InAppWebViewController controller, WebResourceRequest request)
final Future<WebResourceResponse?> Function(
InAppWebViewController controller, WebResourceRequest request)?
androidShouldInterceptRequest;
@override
final Future<WebViewRenderProcessAction> Function(
InAppWebViewController controller, String url)
final Future<WebViewRenderProcessAction?> Function(
InAppWebViewController controller, String? url)?
androidOnRenderProcessUnresponsive;
@override
final Future<WebViewRenderProcessAction> Function(
InAppWebViewController controller, String url)
final Future<WebViewRenderProcessAction?> Function(
InAppWebViewController controller, String? url)?
androidOnRenderProcessResponsive;
@override
final void Function(
InAppWebViewController controller, RenderProcessGoneDetail detail)
InAppWebViewController controller, RenderProcessGoneDetail detail)?
androidOnRenderProcessGone;
@override
final Future<FormResubmissionAction> Function(
InAppWebViewController controller, String url) androidOnFormResubmission;
final Future<FormResubmissionAction?> Function(
InAppWebViewController controller, String? url)? androidOnFormResubmission;
@override
final void Function(
InAppWebViewController controller, double oldScale, double newScale)
InAppWebViewController controller, double oldScale, double newScale)?
androidOnScaleChanged;
@override
final void Function(InAppWebViewController controller, Uint8List icon)
final void Function(InAppWebViewController controller, Uint8List icon)?
androidOnReceivedIcon;
@override
final void Function(
InAppWebViewController controller, String url, bool precomposed)
InAppWebViewController controller, String url, bool precomposed)?
androidOnReceivedTouchIconUrl;
@override
final Future<JsBeforeUnloadResponse> Function(
final Future<JsBeforeUnloadResponse?> Function(
InAppWebViewController controller,
JsBeforeUnloadRequest jsBeforeUnloadRequest) androidOnJsBeforeUnload;
JsBeforeUnloadRequest jsBeforeUnloadRequest)? androidOnJsBeforeUnload;
@override
final void Function(
InAppWebViewController controller, LoginRequest loginRequest)
InAppWebViewController controller, LoginRequest loginRequest)?
androidOnReceivedLoginRequest;
}

View File

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

View File

@ -2,7 +2,6 @@ import 'dart:async';
import 'dart:collection';
import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'context_menu.dart';
@ -15,23 +14,23 @@ import 'types.dart';
///The [webViewController] field can be used to access the [InAppWebViewController] API.
class InAppBrowser {
///Browser's UUID.
String uuid;
late String uuid;
///Context menu used by the browser. It should be set before opening the browser.
ContextMenu contextMenu;
ContextMenu? contextMenu;
Map<String, JavaScriptHandlerCallback> javaScriptHandlersMap =
HashMap<String, JavaScriptHandlerCallback>();
bool _isOpened = false;
MethodChannel _channel;
late MethodChannel _channel;
static const MethodChannel _sharedChannel =
const MethodChannel('com.pichillilorenzo/flutter_inappbrowser');
/// WebView Controller that can be used to access the [InAppWebViewController] API.
InAppWebViewController webViewController;
late InAppWebViewController webViewController;
///The window id of a [CreateWindowRequest.windowId].
final int windowId;
final int? windowId;
///
InAppBrowser({this.windowId}) {
@ -67,10 +66,10 @@ class InAppBrowser {
///
///[options]: Options for the [InAppBrowser].
Future<void> openUrl(
{@required String url,
{required String url,
Map<String, String> headers = const {},
InAppBrowserClassOptions options}) async {
assert(url != null && url.isNotEmpty);
InAppBrowserClassOptions? options}) async {
assert(url.isNotEmpty);
this.throwIsAlreadyOpened(message: 'Cannot open $url!');
Map<String, dynamic> args = <String, dynamic>{};
@ -117,10 +116,10 @@ class InAppBrowser {
///
///[options]: Options for the [InAppBrowser].
Future<void> openFile(
{@required String assetFilePath,
{required String assetFilePath,
Map<String, String> headers = const {},
InAppBrowserClassOptions options}) async {
assert(assetFilePath != null && assetFilePath.isNotEmpty);
InAppBrowserClassOptions? options}) async {
assert(assetFilePath.isNotEmpty);
this.throwIsAlreadyOpened(message: 'Cannot open $assetFilePath!');
Map<String, dynamic> args = <String, dynamic>{};
@ -143,14 +142,12 @@ class InAppBrowser {
///
///The [options] parameter specifies the options for the [InAppBrowser].
Future<void> openData(
{@required String data,
{required String data,
String mimeType = "text/html",
String encoding = "utf8",
String baseUrl = "about:blank",
String androidHistoryUrl = "about:blank",
InAppBrowserClassOptions options}) async {
assert(data != null);
InAppBrowserClassOptions? options}) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid);
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!
static Future<void> openWithSystemBrowser({@required String url}) async {
assert(url != null && url.isNotEmpty);
static Future<void> openWithSystemBrowser({required String url}) async {
assert(url.isNotEmpty);
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url);
return await _sharedChannel.invokeMethod('openWithSystemBrowser', args);
@ -202,24 +199,24 @@ class InAppBrowser {
}
///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();
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('options', () => options?.toMap() ?? {});
args.putIfAbsent('options', () => options.toMap());
await _channel.invokeMethod('setOptions', args);
}
///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();
Map<String, dynamic> args = <String, dynamic>{};
Map<dynamic, dynamic> options =
Map<dynamic, dynamic>? options =
await _channel.invokeMethod('getOptions', args);
if (options != null) {
options = options.cast<String, dynamic>();
return InAppBrowserClassOptions.fromMap(options);
return InAppBrowserClassOptions.fromMap(options as Map<String, dynamic>);
}
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 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].
///
///**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
void onLoadStop(String url) {}
void onLoadStop(String? 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 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.
///
@ -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 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.
///
@ -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 iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455641-webview
// ignore: missing_return
Future<ShouldOverrideUrlLoadingAction> shouldOverrideUrlLoading(
Future<ShouldOverrideUrlLoadingAction?>? shouldOverrideUrlLoading(
ShouldOverrideUrlLoadingRequest shouldOverrideUrlLoadingRequest) {}
///Event fired when the [InAppBrowser] webview loads a resource.
@ -335,8 +331,7 @@ class InAppBrowser {
///[url] represents the url of the request.
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkurlschemehandler
// ignore: missing_return
Future<CustomSchemeResponse> onLoadResourceCustomScheme(
Future<CustomSchemeResponse?>? onLoadResourceCustomScheme(
String scheme, String url) {}
///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 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.
///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 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.
///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 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.
///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 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.
///
@ -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 iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview
// ignore: missing_return
Future<HttpAuthResponse> onReceivedHttpAuthRequest(
Future<HttpAuthResponse?>? onReceivedHttpAuthRequest(
HttpAuthChallenge challenge) {}
///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 iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview
// ignore: missing_return
Future<ServerTrustAuthResponse> onReceivedServerTrustAuthRequest(
Future<ServerTrustAuthResponse?>? onReceivedServerTrustAuthRequest(
ServerTrustChallenge challenge) {}
///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 iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview
// ignore: missing_return
Future<ClientCertResponse> onReceivedClientCertRequest(
Future<ClientCertResponse?>? onReceivedClientCertRequest(
ClientCertChallenge challenge) {}
///Event fired as find-on-page operations progress.
@ -477,8 +465,7 @@ class InAppBrowser {
///[ajaxRequest] represents the `XMLHttpRequest`.
///
///**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.
///It gives the host application a chance to abort the request.
@ -486,8 +473,7 @@ class InAppBrowser {
///[ajaxRequest] represents the [XMLHttpRequest].
///
///**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.
///It gives the host application a chance to abort the request.
@ -495,8 +481,7 @@ class InAppBrowser {
///[ajaxRequest] represents the [XMLHttpRequest].
///
///**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).
///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.
///
///**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.
///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.
///
///**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.
///
///[url] represents the url on which is called.
///
///**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.
///
@ -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 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.
///
///[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)
void onTitleChanged(String title) {}
void onTitleChanged(String? title) {}
///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.
@ -578,9 +562,8 @@ class InAppBrowser {
///**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)
// ignore: missing_return
Future<SafeBrowsingResponse> androidOnSafeBrowsingHit(
String url, SafeBrowsingThreat threatType) {}
Future<SafeBrowsingResponse?>? androidOnSafeBrowsingHit(
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.
///
@ -591,8 +574,7 @@ class InAppBrowser {
///**NOTE**: available only on Android 23+.
///
///**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) {}
///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.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onGeolocationPermissionsShowPrompt(java.lang.String,%20android.webkit.GeolocationPermissions.Callback)
Future<GeolocationPermissionShowPromptResponse>
// ignore: missing_return
Future<GeolocationPermissionShowPromptResponse?>?
androidOnGeolocationPermissionsShowPrompt(String origin) {}
///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**:
///- 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)
Future<WebResourceResponse>
// ignore: missing_return
Future<WebResourceResponse?>?
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.
@ -655,9 +635,8 @@ class InAppBrowser {
///**NOTE**: available only on Android 29+.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewRenderProcessClient#onRenderProcessUnresponsive(android.webkit.WebView,%20android.webkit.WebViewRenderProcess)
Future<WebViewRenderProcessAction>
// ignore: missing_return
androidOnRenderProcessUnresponsive(String url) {}
Future<WebViewRenderProcessAction?>?
androidOnRenderProcessUnresponsive(String? url) {}
///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+.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewRenderProcessClient#onRenderProcessResponsive(android.webkit.WebView,%20android.webkit.WebViewRenderProcess)
Future<WebViewRenderProcessAction>
// ignore: missing_return
androidOnRenderProcessResponsive(String url) {}
Future<WebViewRenderProcessAction?>?
androidOnRenderProcessResponsive(String? url) {}
///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.
@ -690,9 +668,8 @@ class InAppBrowser {
///**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)
Future<FormResubmissionAction>
// ignore: missing_return
androidOnFormResubmission(String url) {}
Future<FormResubmissionAction?>?
androidOnFormResubmission(String? url) {}
///Event fired when the scale applied to the WebView has changed.
///
@ -746,8 +723,7 @@ class InAppBrowser {
///**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)
// ignore: missing_return
Future<JsBeforeUnloadResponse> androidOnJsBeforeUnload(
Future<JsBeforeUnloadResponse?>? androidOnJsBeforeUnload(
JsBeforeUnloadRequest jsBeforeUnloadRequest) {}
///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`.
class InAppLocalhostServer {
HttpServer _server;
HttpServer? _server;
int _port = 8080;
InAppLocalhostServer({int port = 8080}) {
@ -38,7 +38,7 @@ class InAppLocalhostServer {
this._server = server;
server.listen((HttpRequest request) async {
var body = List<int>();
var body = [] as List<int>;
var path = request.requestedUri.path;
path = (path.startsWith('/')) ? path.substring(1) : path;
path += (path.endsWith('/')) ? 'index.html' : '';
@ -77,7 +77,7 @@ class InAppLocalhostServer {
///Closes the server.
Future<void> close() async {
if (this._server != null) {
await this._server.close(force: true);
await this._server!.close(force: true);
print('Server running on http://localhost:$_port closed');
this._server = null;
}

View File

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

View File

@ -1,7 +1,5 @@
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'in_app_webview_controller.dart';
import 'types.dart';
@ -14,13 +12,13 @@ class WebStorage {
///Represents `window.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 WebStorageItem {
///Item key.
String key;
String? key;
///Item 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.
///It is used by [LocalStorage] and [SessionStorage].
class Storage {
InAppWebViewController _controller;
late InAppWebViewController _controller;
///The web storage type: `window.sessionStorage` or `window.localStorage`.
WebStorageType webStorageType;
Storage(InAppWebViewController controller, this.webStorageType) {
assert(controller != null && this.webStorageType != null);
this._controller = controller;
}
///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: """
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.
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);
await _controller.evaluateJavascript(source: """
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.
Future<dynamic> getItem({@required String key}) async {
Future<dynamic> getItem({required String key}) async {
var itemValue = await _controller.evaluateJavascript(source: """
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.
Future<void> removeItem({@required String key}) async {
Future<void> removeItem({required String key}) async {
await _controller.evaluateJavascript(source: """
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.
///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: """
window.$webStorageType.key($index);
""");

View File

@ -1,6 +1,5 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'types.dart';
@ -11,7 +10,7 @@ import 'types.dart';
///
///**NOTE for iOS**: available from iOS 9.0+.
class WebStorageManager {
static WebStorageManager _instance;
static WebStorageManager? _instance;
static const MethodChannel _channel = const MethodChannel(
'com.pichillilorenzo/flutter_inappwebview_webstoragemanager');
@ -20,13 +19,13 @@ class WebStorageManager {
///Gets the WebStorage manager shared instance.
static WebStorageManager instance() {
return (_instance != null) ? _instance : _init();
return (_instance != null) ? _instance! : _init();
}
static WebStorageManager _init() {
_channel.setMethodCallHandler(_handleMethod);
_instance = new WebStorageManager();
return _instance;
return _instance!;
}
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].
///The origin is specified using its string representation.
Future<void> deleteOrigin({@required String origin}) async {
assert(origin != null);
Future<void> deleteOrigin({required String origin}) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("origin", () => origin);
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].
///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.
Future<int> getQuotaForOrigin({@required String origin}) async {
assert(origin != null);
Future<int> getQuotaForOrigin({required String origin}) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("origin", () => origin);
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].
///The amount is given in bytes and the origin is specified using its string representation.
Future<int> getUsageForOrigin({@required String origin}) async {
assert(origin != null);
Future<int> getUsageForOrigin({required String origin}) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("origin", () => origin);
return await WebStorageManager._channel
@ -101,8 +97,7 @@ class IOSWebStorageManager {
///
///[dataTypes] represents the website data types to fetch records for.
Future<List<IOSWKWebsiteDataRecord>> fetchDataRecords(
{@required Set<IOSWKWebsiteDataType> dataTypes}) async {
assert(dataTypes != null);
{required Set<IOSWKWebsiteDataType> dataTypes}) async {
List<IOSWKWebsiteDataRecord> recordList = [];
List<String> dataTypesList = [];
for (var dataType in dataTypes) {
@ -116,8 +111,11 @@ class IOSWebStorageManager {
for (var record in records) {
List<String> dataTypesString = record["dataTypes"].cast<String>();
Set<IOSWKWebsiteDataType> dataTypes = Set();
for (var dataType in dataTypesString) {
dataTypes.add(IOSWKWebsiteDataType.fromValue(dataType));
for (var dataTypeValue in dataTypesString) {
var dataType = IOSWKWebsiteDataType.fromValue(dataTypeValue);
if (dataType != null) {
dataTypes.add(dataType);
}
}
recordList.add(IOSWKWebsiteDataRecord(
displayName: record["displayName"], dataTypes: dataTypes));
@ -131,10 +129,8 @@ class IOSWebStorageManager {
///
///[dataRecords] represents the website data records to delete website data for.
Future<void> removeDataFor(
{@required Set<IOSWKWebsiteDataType> dataTypes,
@required List<IOSWKWebsiteDataRecord> dataRecords}) async {
assert(dataTypes != null && dataRecords != null);
{required Set<IOSWKWebsiteDataType> dataTypes,
required List<IOSWKWebsiteDataRecord> dataRecords}) async {
List<String> dataTypesList = [];
for (var dataType in dataTypes) {
dataTypesList.add(dataType.toValue());
@ -157,10 +153,8 @@ class IOSWebStorageManager {
///
///[date] represents a date. All website data modified after this date will be removed.
Future<void> removeDataModifiedSince(
{@required Set<IOSWKWebsiteDataType> dataTypes,
@required DateTime date}) async {
assert(dataTypes != null && date != null);
{required Set<IOSWKWebsiteDataType> dataTypes,
required DateTime date}) async {
List<String> dataTypesList = [];
for (var dataType in dataTypes) {
dataTypesList.add(dataType.toValue());

View File

@ -7,20 +7,20 @@ import 'in_app_webview_controller.dart';
import 'webview_options.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 {
///The window id of a [CreateWindowRequest.windowId].
final int windowId;
final int? windowId;
///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].
///
///**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
final void Function(InAppWebViewController controller, String url)
final void Function(InAppWebViewController controller, String? url)?
onLoadStart;
///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 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].
///
///**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
final void Function(InAppWebViewController controller, String url, int code,
String message) onLoadError;
final void Function(InAppWebViewController controller, String? url, int code,
String message)? onLoadError;
///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 iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview
final void Function(InAppWebViewController controller, String url,
int statusCode, String description) onLoadHttpError;
final void Function(InAppWebViewController controller, String? url,
int statusCode, String description)? onLoadHttpError;
///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)
final void Function(InAppWebViewController controller, int progress)
final void Function(InAppWebViewController controller, int progress)?
onProgressChanged;
///Event fired when the [WebView] receives a [ConsoleMessage].
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onConsoleMessage(android.webkit.ConsoleMessage)
final void Function(
InAppWebViewController controller, ConsoleMessage consoleMessage)
InAppWebViewController controller, ConsoleMessage consoleMessage)?
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.
@ -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 iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455641-webview
final Future<ShouldOverrideUrlLoadingAction> Function(
final Future<ShouldOverrideUrlLoadingAction?> Function(
InAppWebViewController controller,
ShouldOverrideUrlLoadingRequest shouldOverrideUrlLoadingRequest)
ShouldOverrideUrlLoadingRequest shouldOverrideUrlLoadingRequest)?
shouldOverrideUrlLoading;
///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`.
final void Function(
InAppWebViewController controller, LoadedResource resource)
InAppWebViewController controller, LoadedResource resource)?
onLoadResource;
///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 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;
///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 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;
///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.
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkurlschemehandler
final Future<CustomSchemeResponse> Function(
InAppWebViewController controller, String scheme, String url)
final Future<CustomSchemeResponse?> Function(
InAppWebViewController controller, String scheme, String url)?
onLoadResourceCustomScheme;
///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 iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1536907-webview
final Future<bool> Function(InAppWebViewController controller,
CreateWindowRequest createWindowRequest) onCreateWindow;
final Future<bool?> Function(InAppWebViewController controller,
CreateWindowRequest createWindowRequest)? onCreateWindow;
///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.
@ -169,15 +169,15 @@ abstract class 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
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.
///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.
///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.
///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 iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1537406-webview
final Future<JsAlertResponse> Function(
InAppWebViewController controller, JsAlertRequest jsAlertRequest)
final Future<JsAlertResponse?> Function(
InAppWebViewController controller, JsAlertRequest jsAlertRequest)?
onJsAlert;
///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 iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1536489-webview
final Future<JsConfirmResponse> Function(
InAppWebViewController controller, JsConfirmRequest jsConfirmRequest)
final Future<JsConfirmResponse?> Function(
InAppWebViewController controller, JsConfirmRequest jsConfirmRequest)?
onJsConfirm;
///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 iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1538086-webview
final Future<JsPromptResponse> Function(
InAppWebViewController controller, JsPromptRequest jsPromptRequest)
final Future<JsPromptResponse?> Function(
InAppWebViewController controller, JsPromptRequest jsPromptRequest)?
onJsPrompt;
///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 iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview
final Future<HttpAuthResponse> Function(
InAppWebViewController controller, HttpAuthChallenge challenge)
final Future<HttpAuthResponse?> Function(
InAppWebViewController controller, HttpAuthChallenge challenge)?
onReceivedHttpAuthRequest;
///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 iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview
final Future<ServerTrustAuthResponse> Function(
InAppWebViewController controller, ServerTrustChallenge challenge)
final Future<ServerTrustAuthResponse?> Function(
InAppWebViewController controller, ServerTrustChallenge challenge)?
onReceivedServerTrustAuthRequest;
///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 iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview
final Future<ClientCertResponse> Function(
InAppWebViewController controller, ClientCertChallenge challenge)
final Future<ClientCertResponse?> Function(
InAppWebViewController controller, ClientCertChallenge challenge)?
onReceivedClientCertRequest;
///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)
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.
///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
///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.
final Future<AjaxRequest> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest)
final Future<AjaxRequest?> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest)?
shouldInterceptAjaxRequest;
///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
///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.
final Future<AjaxRequestAction> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest)
final Future<AjaxRequestAction?> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest)?
onAjaxReadyStateChange;
///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
///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.
final Future<AjaxRequestAction> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest)
final Future<AjaxRequestAction?> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest)?
onAjaxProgress;
///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
///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.
final Future<FetchRequest> Function(
InAppWebViewController controller, FetchRequest fetchRequest)
final Future<FetchRequest?> Function(
InAppWebViewController controller, FetchRequest fetchRequest)?
shouldInterceptFetchRequest;
///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)
final void Function(
InAppWebViewController controller, String url, bool androidIsReload)
InAppWebViewController controller, String? url, bool? androidIsReload)?
onUpdateVisitedHistory;
///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.
///
///**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.
///
@ -350,21 +350,21 @@ abstract class WebView {
///
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uilongpressgesturerecognizer
final void Function(InAppWebViewController controller,
InAppWebViewHitTestResult hitTestResult) onLongPressHitTestResult;
InAppWebViewHitTestResult hitTestResult)? onLongPressHitTestResult;
///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 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.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onHideCustomView()
///
///**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.
///
@ -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 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;
///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.
///
///**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;
///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+.
///
///**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,
String url, SafeBrowsingThreat threatType) androidOnSafeBrowsingHit;
final Future<SafeBrowsingResponse?> Function(InAppWebViewController controller,
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.
///
@ -409,10 +409,10 @@ abstract class WebView {
///**NOTE**: available only on Android 23+.
///
///**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,
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.
///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.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onGeolocationPermissionsShowPrompt(java.lang.String,%20android.webkit.GeolocationPermissions.Callback)
final Future<GeolocationPermissionShowPromptResponse> Function(
InAppWebViewController controller, String origin)
final Future<GeolocationPermissionShowPromptResponse?> Function(
InAppWebViewController controller, String origin)?
androidOnGeolocationPermissionsShowPrompt;
///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.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onGeolocationPermissionsHidePrompt()
final void Function(InAppWebViewController controller)
final void Function(InAppWebViewController controller)?
androidOnGeolocationPermissionsHidePrompt;
///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**:
///- 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)
final Future<WebResourceResponse> Function(
InAppWebViewController controller, WebResourceRequest request)
final Future<WebResourceResponse?> Function(
InAppWebViewController controller, WebResourceRequest request)?
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.
@ -475,8 +475,8 @@ abstract class WebView {
///**NOTE**: available only on Android 29+.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewRenderProcessClient#onRenderProcessUnresponsive(android.webkit.WebView,%20android.webkit.WebViewRenderProcess)
final Future<WebViewRenderProcessAction> Function(
InAppWebViewController controller, String url)
final Future<WebViewRenderProcessAction?> Function(
InAppWebViewController controller, String? url)?
androidOnRenderProcessUnresponsive;
///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+.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewRenderProcessClient#onRenderProcessResponsive(android.webkit.WebView,%20android.webkit.WebViewRenderProcess)
final Future<WebViewRenderProcessAction> Function(
InAppWebViewController controller, String url)
final Future<WebViewRenderProcessAction?> Function(
InAppWebViewController controller, String? url)?
androidOnRenderProcessResponsive;
///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)
final void Function(
InAppWebViewController controller, RenderProcessGoneDetail detail)
InAppWebViewController controller, RenderProcessGoneDetail detail)?
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.
@ -512,8 +512,8 @@ abstract class WebView {
///**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)
final Future<FormResubmissionAction> Function(
InAppWebViewController controller, String url) androidOnFormResubmission;
final Future<FormResubmissionAction?> Function(
InAppWebViewController controller, String? url)? androidOnFormResubmission;
///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)
final void Function(
InAppWebViewController controller, double oldScale, double newScale)
InAppWebViewController controller, double oldScale, double newScale)?
androidOnScaleChanged;
///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.
///
///**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.
///
@ -543,7 +543,7 @@ abstract class WebView {
///**NOTE**: available only on Android.
///
///**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;
///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)
final void Function(
InAppWebViewController controller, String url, bool precomposed)
InAppWebViewController controller, String url, bool precomposed)?
androidOnReceivedTouchIconUrl;
///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.
///
///**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,
JsBeforeUnloadRequest jsBeforeUnloadRequest) androidOnJsBeforeUnload;
JsBeforeUnloadRequest jsBeforeUnloadRequest)? androidOnJsBeforeUnload;
///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)
final void Function(
InAppWebViewController controller, LoginRequest loginRequest)
InAppWebViewController controller, LoginRequest loginRequest)?
androidOnReceivedLoginRequest;
///Invoked when the web view's web content process is terminated.
@ -592,7 +592,7 @@ abstract class WebView {
///**NOTE**: available only on iOS.
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455639-webviewwebcontentprocessdidtermi
final void Function(InAppWebViewController controller)
final void Function(InAppWebViewController controller)?
iosOnWebContentProcessDidTerminate;
///Called when a web view receives a server redirect.
@ -600,26 +600,26 @@ abstract class WebView {
///**NOTE**: available only on iOS.
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455627-webview
final void Function(InAppWebViewController controller)
final void Function(InAppWebViewController controller)?
iosOnDidReceiveServerRedirectForProvisionalNavigation;
///Initial url that will be loaded.
final String initialUrl;
final String? initialUrl;
///Initial asset file that will be loaded. See [InAppWebViewController.loadFile] for explanation.
final String initialFile;
final String? initialFile;
///Initial [InAppWebViewInitialData] that will be loaded.
final InAppWebViewInitialData initialData;
final InAppWebViewInitialData? initialData;
///Initial headers that will be used.
final Map<String, String> initialHeaders;
final Map<String, String>? initialHeaders;
///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.
final ContextMenu contextMenu;
final ContextMenu? contextMenu;
WebView(
{this.windowId,

View File

@ -14,7 +14,7 @@ class WebViewOptions {
}
static WebViewOptions fromMap(Map<String, dynamic> map) {
return null;
return new WebViewOptions();
}
WebViewOptions copy() {
@ -37,7 +37,7 @@ class BrowserOptions {
}
static BrowserOptions fromMap(Map<String, dynamic> map) {
return null;
return new BrowserOptions();
}
BrowserOptions copy() {
@ -60,7 +60,7 @@ class ChromeSafariBrowserOptions {
}
static ChromeSafariBrowserOptions fromMap(Map<String, dynamic> map) {
return null;
return new ChromeSafariBrowserOptions();
}
ChromeSafariBrowserOptions copy() {
@ -105,12 +105,6 @@ class InAppWebViewOptions
///Set to `true` to enable JavaScript. The default value is `true`.
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`.
bool javaScriptCanOpenWindowsAutomatically;
@ -120,7 +114,7 @@ class InAppWebViewOptions
bool mediaPlaybackRequiresUserGesture;
///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`.
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].
///
///**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`.
bool useShouldInterceptAjaxRequest;
@ -152,6 +146,8 @@ class InAppWebViewOptions
///Set to `true` to open a browser window with incognito mode. The default value is `false`.
///
///**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;
///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`.
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(
{this.useShouldOverrideUrlLoading = false,
this.useOnLoadResource = false,
@ -182,7 +200,6 @@ class InAppWebViewOptions
this.userAgent = "",
this.applicationNameForUserAgent = "",
this.javaScriptEnabled = true,
this.debuggingEnabled = false,
this.javaScriptCanOpenWindowsAutomatically = false,
this.mediaPlaybackRequiresUserGesture = true,
this.minimumFontSize,
@ -199,7 +216,9 @@ class InAppWebViewOptions
this.disableVerticalScroll = false,
this.disableHorizontalScroll = false,
this.disableContextMenu = false,
this.supportZoom = true}) {
this.supportZoom = true,
this.allowFileAccessFromFileURLs = false,
this.allowUniversalAccessFromFileURLs = false}) {
if (this.minimumFontSize == null)
this.minimumFontSize = defaultTargetPlatform == TargetPlatform.android ? 8 : 0;
assert(!this.resourceCustomSchemes.contains("http") &&
@ -221,7 +240,6 @@ class InAppWebViewOptions
"userAgent": userAgent,
"applicationNameForUserAgent": applicationNameForUserAgent,
"javaScriptEnabled": javaScriptEnabled,
"debuggingEnabled": debuggingEnabled,
"javaScriptCanOpenWindowsAutomatically":
javaScriptCanOpenWindowsAutomatically,
"mediaPlaybackRequiresUserGesture": mediaPlaybackRequiresUserGesture,
@ -238,13 +256,15 @@ class InAppWebViewOptions
"disableVerticalScroll": disableVerticalScroll,
"disableHorizontalScroll": disableHorizontalScroll,
"disableContextMenu": disableContextMenu,
"supportZoom": supportZoom
"supportZoom": supportZoom,
"allowFileAccessFromFileURLs": allowFileAccessFromFileURLs,
"allowUniversalAccessFromFileURLs": allowUniversalAccessFromFileURLs
};
}
static InAppWebViewOptions fromMap(Map<String, dynamic> map) {
List<ContentBlocker> contentBlockers = [];
List<dynamic> contentBlockersMapList = map["contentBlockers"];
List<dynamic>? contentBlockersMapList = map["contentBlockers"];
if (contentBlockersMapList != null) {
contentBlockersMapList.forEach((contentBlocker) {
contentBlockers.add(ContentBlocker.fromMap(
@ -261,7 +281,6 @@ class InAppWebViewOptions
options.userAgent = map["userAgent"];
options.applicationNameForUserAgent = map["applicationNameForUserAgent"];
options.javaScriptEnabled = map["javaScriptEnabled"];
options.debuggingEnabled = map["debuggingEnabled"];
options.javaScriptCanOpenWindowsAutomatically =
map["javaScriptCanOpenWindowsAutomatically"];
options.mediaPlaybackRequiresUserGesture =
@ -284,6 +303,8 @@ class InAppWebViewOptions
options.disableHorizontalScroll = map["disableHorizontalScroll"];
options.disableContextMenu = map["disableContextMenu"];
options.supportZoom = map["supportZoom"];
options.allowFileAccessFromFileURLs = map["allowFileAccessFromFileURLs"];
options.allowUniversalAccessFromFileURLs = map["allowUniversalAccessFromFileURLs"];
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.
///
///**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`.
bool allowContentAccess;
///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;
///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.
///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`.
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.
///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"`.
String cursiveFontFamily;
@ -387,7 +398,7 @@ class AndroidInAppWebViewOptions
///Disables the action mode menu items according to menuItems flag.
///
///**NOTE**: available on Android 24+.
AndroidActionModeMenuItem disabledActionModeMenuItems;
AndroidActionModeMenuItem? disabledActionModeMenuItems;
///Sets the fantasy font family name. The default value is `"fantasy"`.
String fantasyFontFamily;
@ -398,13 +409,13 @@ class AndroidInAppWebViewOptions
///Set the force dark mode for this WebView. The default value is [AndroidForceDark.FORCE_DARK_OFF].
///
///**NOTE**: available on Android 29+.
AndroidForceDark forceDark;
AndroidForceDark? forceDark;
///Sets whether Geolocation API is enabled. The default value is `true`.
bool geolocationEnabled;
///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.
///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.
///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`.
///Hybrid Composition is supported starting with Flutter v1.20+.
@ -486,11 +497,11 @@ class AndroidInAppWebViewOptions
///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.
///The default value is [AndroidOverScrollMode.OVER_SCROLL_IF_CONTENT_SCROLLS].
AndroidOverScrollMode overScrollMode;
AndroidOverScrollMode? overScrollMode;
///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.
bool networkAvailable;
bool? networkAvailable;
///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.
@ -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,
///then you can use SCROLLBARS_OUTSIDE_OVERLAY or SCROLLBARS_OUTSIDE_INSET.
///The default value is [AndroidScrollBarStyle.SCROLLBARS_INSIDE_OVERLAY].
AndroidScrollBarStyle scrollBarStyle;
AndroidScrollBarStyle? scrollBarStyle;
///Sets the position of the vertical scroll bar.
///The default value is [AndroidVerticalScrollbarPosition.SCROLLBAR_POSITION_DEFAULT].
AndroidVerticalScrollbarPosition verticalScrollbarPosition;
AndroidVerticalScrollbarPosition? verticalScrollbarPosition;
///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.
///The default value is `true`.
bool scrollbarFadingEnabled;
///Defines the scrollbar fade duration in milliseconds.
int scrollBarFadeDuration;
int? scrollBarFadeDuration;
///Sets the renderer priority policy for this WebView.
RendererPriorityPolicy rendererPriorityPolicy;
RendererPriorityPolicy? rendererPriorityPolicy;
///Sets whether the default Android error page should be disabled.
///The default value is `false`.
bool disableDefaultErrorPage;
bool? disableDefaultErrorPage;
AndroidInAppWebViewOptions({
this.textZoom = 100,
@ -533,8 +544,6 @@ class AndroidInAppWebViewOptions
this.mixedContentMode,
this.allowContentAccess = true,
this.allowFileAccess = true,
this.allowFileAccessFromFileURLs = false,
this.allowUniversalAccessFromFileURLs = false,
this.appCachePath,
this.blockNetworkImage = false,
this.blockNetworkLoads = false,
@ -592,8 +601,6 @@ class AndroidInAppWebViewOptions
"mixedContentMode": mixedContentMode?.toValue(),
"allowContentAccess": allowContentAccess,
"allowFileAccess": allowFileAccess,
"allowFileAccessFromFileURLs": allowFileAccessFromFileURLs,
"allowUniversalAccessFromFileURLs": allowUniversalAccessFromFileURLs,
"appCachePath": appCachePath,
"blockNetworkImage": blockNetworkImage,
"blockNetworkLoads": blockNetworkLoads,
@ -651,9 +658,6 @@ class AndroidInAppWebViewOptions
AndroidMixedContentMode.fromValue(map["mixedContentMode"]);
options.allowContentAccess = map["allowContentAccess"];
options.allowFileAccess = map["allowFileAccess"];
options.allowFileAccessFromFileURLs = map["allowFileAccessFromFileURLs"];
options.allowUniversalAccessFromFileURLs =
map["allowUniversalAccessFromFileURLs"];
options.appCachePath = map["appCachePath"];
options.blockNetworkImage = map["blockNetworkImage"];
options.blockNetworkLoads = map["blockNetworkLoads"];
@ -899,8 +903,11 @@ class IOSInAppWebViewOptions
List<IOSWKDataDetectorTypes> dataDetectorTypes = [];
List<String> dataDetectorTypesList =
List<String>.from(map["dataDetectorTypes"] ?? []);
dataDetectorTypesList.forEach((dataDetectorType) {
dataDetectorTypes.add(IOSWKDataDetectorTypes.fromValue(dataDetectorType));
dataDetectorTypesList.forEach((dataDetectorTypeValue) {
var dataDetectorType = IOSWKDataDetectorTypes.fromValue(dataDetectorTypeValue);
if (dataDetectorType != null) {
dataDetectorTypes.add(dataDetectorType);
}
});
IOSInAppWebViewOptions options = IOSInAppWebViewOptions();
@ -920,7 +927,7 @@ class IOSInAppWebViewOptions
options.isFraudulentWebsiteWarningEnabled =
map["isFraudulentWebsiteWarningEnabled"];
options.selectionGranularity =
IOSWKSelectionGranularity.fromValue(map["selectionGranularity"]);
IOSWKSelectionGranularity.fromValue(map["selectionGranularity"])!;
options.dataDetectorTypes = dataDetectorTypes;
options.sharedCookiesEnabled = map["sharedCookiesEnabled"];
options.automaticallyAdjustsScrollIndicatorInsets =
@ -928,7 +935,7 @@ class IOSInAppWebViewOptions
options.accessibilityIgnoresInvertColors =
map["accessibilityIgnoresInvertColors"];
options.decelerationRate =
IOSUIScrollViewDecelerationRate.fromValue(map["decelerationRate"]);
IOSUIScrollViewDecelerationRate.fromValue(map["decelerationRate"])!;
options.alwaysBounceVertical = map["alwaysBounceVertical"];
options.alwaysBounceHorizontal = map["alwaysBounceHorizontal"];
options.scrollsToTop = map["scrollsToTop"];
@ -937,7 +944,7 @@ class IOSInAppWebViewOptions
options.minimumZoomScale = map["minimumZoomScale"];
options.contentInsetAdjustmentBehavior =
IOSUIScrollViewContentInsetAdjustmentBehavior.fromValue(
map["contentInsetAdjustmentBehavior"]);
map["contentInsetAdjustmentBehavior"])!;
return options;
}
@ -1127,9 +1134,9 @@ class IOSInAppBrowserOptions implements BrowserOptions, IosOptions {
options.closeButtonCaption = map["closeButtonCaption"];
options.closeButtonColor = map["closeButtonColor"];
options.presentationStyle =
IOSUIModalPresentationStyle.fromValue(map["presentationStyle"]);
IOSUIModalPresentationStyle.fromValue(map["presentationStyle"])!;
options.transitionStyle =
IOSUIModalTransitionStyle.fromValue(map["transitionStyle"]);
IOSUIModalTransitionStyle.fromValue(map["transitionStyle"])!;
options.spinner = map["spinner"];
return options;
}
@ -1173,7 +1180,7 @@ class AndroidChromeCustomTabsOptions
///value of null, all components in all applications will considered.
///If non-null, the Intent can only match the components in the given
///application package.
String packageName;
String? packageName;
///Set to `true` to enable Keep Alive. The default value is `false`.
bool keepAliveEnabled;
@ -1285,13 +1292,13 @@ class IOSSafariOptions implements ChromeSafariBrowserOptions, IosOptions {
options.entersReaderIfAvailable = map["entersReaderIfAvailable"];
options.barCollapsingEnabled = map["barCollapsingEnabled"];
options.dismissButtonStyle =
IOSSafariDismissButtonStyle.fromValue(map["dismissButtonStyle"]);
IOSSafariDismissButtonStyle.fromValue(map["dismissButtonStyle"])!;
options.preferredBarTintColor = map["preferredBarTintColor"];
options.preferredControlTintColor = map["preferredControlTintColor"];
options.presentationStyle =
IOSUIModalPresentationStyle.fromValue(map["presentationStyle"]);
IOSUIModalPresentationStyle.fromValue(map["presentationStyle"])!;
options.transitionStyle =
IOSUIModalTransitionStyle.fromValue(map["transitionStyle"]);
IOSUIModalTransitionStyle.fromValue(map["transitionStyle"])!;
return options;
}

View File

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

View File

@ -1,17 +1,22 @@
name: flutter_inappwebview
description: A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window.
version: 5.0.0
version: 5.0.0-nullsafety.0
homepage: https://github.com/pichillilorenzo/flutter_inappwebview
environment:
sdk: ">=2.7.0 <3.0.0"
flutter: ">=1.12.13+hotfix.5"
sdk: ">=2.12.0-0 <3.0.0"
flutter: ">=1.22.0"
dependencies:
flutter:
sdk: flutter
uuid: ^2.0.0
mime: ^0.9.6+2
uuid: ^3.0.0-nullsafety.0
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
# following page: https://www.dartlang.org/tools/pub/pubspec

View File

@ -1,4 +1,5 @@
#!/bin/bash
# on linux/macOS local IP can be found using $(ipconfig getifaddr en0)
export NODE_SERVER_IP=$1
dart tool/env.dart
@ -7,5 +8,5 @@ node index.js &
flutter clean
cd ../example
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)

View File

@ -7,6 +7,6 @@ Future<void> main() async {
'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)};');
}