Update README.md, Update missing docs, Fix pub.dev Health suggestions and Analysis suggestions, v3.4.0+1

This commit is contained in:
Lorenzo Pichilli 2020-06-22 01:17:35 +02:00
parent 993c6f60f9
commit 477b94e03f
12 changed files with 71 additions and 12 deletions

View File

@ -1,3 +1,9 @@
## 3.4.0+1
- Update README.md
- Update missing docs
- Fix pub.dev Health suggestions and Analysis suggestions
## 3.4.0 ## 3.4.0
- Added `requestFocusNodeHref`, `requestImageRef`, `getMetaTags`, `getMetaThemeColor`, `getScrollX`, `getScrollY`, `getCertificate` webview methods - Added `requestFocusNodeHref`, `requestImageRef`, `getMetaTags`, `getMetaThemeColor`, `getScrollX`, `getScrollY`, `getCertificate` webview methods

View File

@ -412,8 +412,24 @@ Screenshots:
* `getMetaThemeColor`: Returns an instance of `Color` representing the `content` value of the `<meta name="theme-color" content="">` tag of the current WebView, if available, otherwise `null`. * `getMetaThemeColor`: Returns an instance of `Color` representing the `content` value of the `<meta name="theme-color" content="">` tag of the current WebView, if available, otherwise `null`.
* `getScrollX`: Returns the scrolled left position of the current WebView. * `getScrollX`: Returns the scrolled left position of the current WebView.
* `getScrollY`: Returns the scrolled top position of the current WebView. * `getScrollY`: Returns the scrolled top position of the current WebView.
* `getCertificate`: Gets the SSL certificate for the main top-level page or null if there is no certificate (the site is not secure).
* `static getDefaultUserAgent`: Gets the default user agent. * `static getDefaultUserAgent`: Gets the default user agent.
##### `InAppWebViewController.webStorage`
`InAppWebViewController.webStorage` provides access to the JavaScript [Web Storage API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API):
* `WebStorage.localStorage`: provides access to `window.localStorage`;
* `WebStorage.sessionStorage`: provides access to `window.localStorage`.
Methods available:
* `length`: Returns an integer representing the number of data items stored in the Storage object.
* `setItem({@required String key, @required dynamic value})`: When passed a `key` name and `value`, will add that key to the storage, or update that key's value if it already exists.
* `getItem({@required String key})`: When passed a `key` name, will return that key's value, or `null` if the key does not exist, in the given Storage object.
* `removeItem({@required String key})`: When passed a `key` name, will remove that key from the given Storage object if it exists.
* `getItems`: Returns the list of all items from the given Storage object.
* `clear`: Clears all keys stored in a given Storage object.
* `key({@required int index})`: When passed a number `index`, returns the name of the nth key in a given Storage object.
##### `InAppWebViewController` Android-specific methods ##### `InAppWebViewController` Android-specific methods
Android-specific methods can be called using the `InAppWebViewController.android` attribute. Android-specific methods can be called using the `InAppWebViewController.android` attribute.

View File

@ -1 +1 @@
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"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":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","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":[]}],"android":[{"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":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","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":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.4+3/","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":[]},"dependencyGraph":[{"name":"e2e","dependencies":[]},{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","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":[]}],"date_created":"2020-06-22 00:03:09.205244","version":"1.17.4"} {"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"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":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","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":[]}],"android":[{"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":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","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":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.4+3/","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":[]},"dependencyGraph":[{"name":"e2e","dependencies":[]},{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","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":[]}],"date_created":"2020-06-22 00:25:30.717386","version":"1.17.4"}

View File

@ -26,7 +26,6 @@
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappwebview/example/.dart_tool" /> <excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappwebview/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappwebview/example/.pub" /> <excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappwebview/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappwebview/example/build" /> <excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappwebview/example/build" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappwebview/example/ios/Flutter/App.framework/flutter_assets/packages" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/Flutter/App.framework/flutter_assets/packages" /> <excludeFolder url="file://$MODULE_DIR$/example/ios/Flutter/App.framework/flutter_assets/packages" />
<excludeFolder url="file://$MODULE_DIR$/flutter_inappbrowser_tests/.dart_tool" /> <excludeFolder url="file://$MODULE_DIR$/flutter_inappbrowser_tests/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/flutter_inappbrowser_tests/.pub" /> <excludeFolder url="file://$MODULE_DIR$/flutter_inappbrowser_tests/.pub" />

View File

@ -242,7 +242,9 @@ class ASN1DERDecoder {
// if YY >= 50 use 19xx, if YY < 50 use 20xx // if YY >= 50 use 19xx, if YY < 50 use 20xx
var year = int.parse(utc.substring(0, 2), radix: 10); var year = int.parse(utc.substring(0, 2), radix: 10);
year = (year >= 50) ? 1900 + year : 2000 + year; year = (year >= 50) ? 1900 + year : 2000 + year;
// ignore: non_constant_identifier_names
var MM = int.parse(utc.substring(2, 4), radix: 10); var MM = int.parse(utc.substring(2, 4), radix: 10);
// ignore: non_constant_identifier_names
var DD = int.parse(utc.substring(4, 6), radix: 10); var DD = int.parse(utc.substring(4, 6), radix: 10);
var hh = int.parse(utc.substring(6, 8), radix: 10); var hh = int.parse(utc.substring(6, 8), radix: 10);
var mm = int.parse(utc.substring(8, 10), radix: 10); var mm = int.parse(utc.substring(8, 10), radix: 10);
@ -328,8 +330,11 @@ class ASN1DERDecoder {
} }
// if YY >= 50 use 19xx, if YY < 50 use 20xx // if YY >= 50 use 19xx, if YY < 50 use 20xx
// ignore: non_constant_identifier_names
var YYYY = int.parse(gentime.substring(0, 4), radix: 10); var YYYY = int.parse(gentime.substring(0, 4), radix: 10);
// ignore: non_constant_identifier_names
var MM = int.parse(gentime.substring(4, 6), radix: 10); var MM = int.parse(gentime.substring(4, 6), radix: 10);
// ignore: non_constant_identifier_names
var DD = int.parse(gentime.substring(6, 8), radix: 10); var DD = int.parse(gentime.substring(6, 8), radix: 10);
var hh = int.parse(gentime.substring(8, 10), radix: 10); var hh = int.parse(gentime.substring(8, 10), radix: 10);
var mm = int.parse(gentime.substring(10, 12), radix: 10); var mm = int.parse(gentime.substring(10, 12), radix: 10);

View File

@ -25,6 +25,7 @@ class ASN1IdentifierClass {
static const CONTEXT_SPECIFIC = const ASN1IdentifierClass._internal(0x80); static const CONTEXT_SPECIFIC = const ASN1IdentifierClass._internal(0x80);
static const PRIVATE = const ASN1IdentifierClass._internal(0xC0); static const PRIVATE = const ASN1IdentifierClass._internal(0xC0);
@override
String toString() { String toString() {
switch (this.toValue()) { switch (this.toValue()) {
case 0x00: case 0x00:
@ -36,6 +37,7 @@ class ASN1IdentifierClass {
case 0xC0: case 0xC0:
return "PRIVATE"; return "PRIVATE";
} }
return "";
} }
bool operator ==(value) => value == _value; bool operator ==(value) => value == _value;
@ -123,6 +125,7 @@ class ASN1IdentifierTagNumber {
static const CHARACTER_STRING = const ASN1IdentifierTagNumber._internal(0x1D); static const CHARACTER_STRING = const ASN1IdentifierTagNumber._internal(0x1D);
static const BMP_STRING = const ASN1IdentifierTagNumber._internal(0x1E); static const BMP_STRING = const ASN1IdentifierTagNumber._internal(0x1E);
@override
String toString() { String toString() {
switch (this.toValue()) { switch (this.toValue()) {
case 0x00: case 0x00:
@ -184,6 +187,7 @@ class ASN1IdentifierTagNumber {
case 0x1E: case 0x1E:
return "BMP_STRING"; return "BMP_STRING";
} }
return "";
} }
bool operator ==(value) => value == _value; bool operator ==(value) => value == _value;

View File

@ -1,6 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:collection'; import 'dart:collection';
import 'dart:io';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';

View File

@ -50,7 +50,10 @@ class InAppWebViewController {
// ignore: unused_field // ignore: unused_field
dynamic _id; dynamic _id;
// ignore: unused_field
String _inAppBrowserUuid; String _inAppBrowserUuid;
InAppBrowser _inAppBrowser; InAppBrowser _inAppBrowser;
///Android controller that contains only android-specific methods ///Android controller that contains only android-specific methods
@ -59,6 +62,7 @@ class InAppWebViewController {
///iOS controller that contains only ios-specific methods ///iOS controller that contains only ios-specific methods
IOSInAppWebViewController ios; IOSInAppWebViewController ios;
///Provides access to the JavaScript [Web Storage API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API): `window.sessionStorage` and `window.localStorage`.
WebStorage webStorage; WebStorage webStorage;
InAppWebViewController(dynamic id, WebView webview) { InAppWebViewController(dynamic id, WebView webview) {
@ -233,9 +237,9 @@ class InAppWebViewController {
case "onGeolocationPermissionsHidePrompt": case "onGeolocationPermissionsHidePrompt":
if (_webview != null && if (_webview != null &&
_webview.androidOnGeolocationPermissionsHidePrompt != null) _webview.androidOnGeolocationPermissionsHidePrompt != null)
await _webview.androidOnGeolocationPermissionsHidePrompt(this); _webview.androidOnGeolocationPermissionsHidePrompt(this);
else if (_inAppBrowser != null) else if (_inAppBrowser != null)
await _inAppBrowser.androidOnGeolocationPermissionsHidePrompt(); _inAppBrowser.androidOnGeolocationPermissionsHidePrompt();
break; break;
case "shouldInterceptRequest": case "shouldInterceptRequest":
String url = call.arguments["url"]; String url = call.arguments["url"];
@ -955,7 +959,7 @@ class InAppWebViewController {
var faviconUrl = url.scheme + "://" + url.host + "/favicon.ico"; var faviconUrl = url.scheme + "://" + url.host + "/favicon.ico";
await client.headUrl(Uri.parse(faviconUrl)); await client.headUrl(Uri.parse(faviconUrl));
favicons.add(Favicon(url: faviconUrl, rel: "shortcut icon")); favicons.add(Favicon(url: faviconUrl, rel: "shortcut icon"));
} catch (e, stacktrace) { } catch (e) {
print("/favicon.ico file not found: " + e.toString()); print("/favicon.ico file not found: " + e.toString());
// print(stacktrace); // print(stacktrace);
} }
@ -972,7 +976,7 @@ class InAppWebViewController {
manifestResponse = await manifestRequest.close(); manifestResponse = await manifestRequest.close();
manifestFound = manifestResponse.statusCode == 200 && manifestFound = manifestResponse.statusCode == 200 &&
manifestResponse.headers.contentType?.mimeType == "application/json"; manifestResponse.headers.contentType?.mimeType == "application/json";
} catch (e, stacktrace) { } catch (e) {
print("Manifest file not found: " + e.toString()); print("Manifest file not found: " + e.toString());
// print(stacktrace); // print(stacktrace);
} }

View File

@ -970,8 +970,6 @@ class ServerTrustChallenge {
IOSSslError iosError; IOSSslError iosError;
///The message associated to the [androidError]/[iosError]. ///The message associated to the [androidError]/[iosError].
///
///**NOTE**: on iOS this value is always an empty string.
String message; String message;
///The SSL certificate used for this challenge. ///The SSL certificate used for this challenge.
@ -4129,8 +4127,11 @@ class SslCertificateDName {
SslCertificateDName( SslCertificateDName(
// ignore: non_constant_identifier_names // ignore: non_constant_identifier_names
{this.CName = "", {this.CName = "",
// ignore: non_constant_identifier_names
this.DName = "", this.DName = "",
// ignore: non_constant_identifier_names
this.OName = "", this.OName = "",
// ignore: non_constant_identifier_names
this.UName = ""}); this.UName = ""});
static SslCertificateDName fromMap(Map<String, dynamic> map) { static SslCertificateDName fromMap(Map<String, dynamic> map) {

View File

@ -5,15 +5,23 @@ import 'package:flutter/foundation.dart';
import 'in_app_webview_controller.dart'; import 'in_app_webview_controller.dart';
import 'types.dart'; import 'types.dart';
///Class that provides access to the JavaScript [Web Storage API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API): `window.sessionStorage` and `window.localStorage`.
///It used by [InAppWebViewController.webStorage].
class WebStorage { class WebStorage {
///Represents `window.localStorage`.
LocalStorage localStorage; LocalStorage localStorage;
///Represents `window.sessionStorage`.
SessionStorage sessionStorage; SessionStorage sessionStorage;
WebStorage({@required this.localStorage, @required this.sessionStorage}); WebStorage({@required this.localStorage, @required this.sessionStorage});
} }
///Class that represents a single web storage item of the JavaScript `window.sessionStorage` and `window.localStorage` objects.
class WebStorageItem { class WebStorageItem {
///Item key.
String key; String key;
///Item value.
dynamic value; dynamic value;
WebStorageItem({this.key, this.value}); WebStorageItem({this.key, this.value});
@ -35,8 +43,13 @@ 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 { class Storage {
InAppWebViewController _controller; InAppWebViewController _controller;
///The web storage type: `window.sessionStorage` or `window.localStorage`.
WebStorageType webStorageType; WebStorageType webStorageType;
Storage(InAppWebViewController controller, this.webStorageType) { Storage(InAppWebViewController controller, this.webStorageType) {
@ -44,6 +57,7 @@ class Storage {
this._controller = controller; 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: """ var result = await _controller.evaluateJavascript(source: """
window.$webStorageType.length; window.$webStorageType.length;
@ -51,6 +65,7 @@ class Storage {
return result != null ? int.parse(json.decode(result)) : null; return result != null ? int.parse(json.decode(result)) : null;
} }
///When passed a [key] name and [value], will add that key to the storage, or update that key's value if it already exists.
Future<void> setItem({@required String key, @required dynamic value}) async { Future<void> setItem({@required String key, @required dynamic value}) async {
var encodedValue = json.encode(value); var encodedValue = json.encode(value);
await _controller.evaluateJavascript(source: """ await _controller.evaluateJavascript(source: """
@ -58,6 +73,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: """ var itemValue = await _controller.evaluateJavascript(source: """
window.$webStorageType.getItem("$key"); window.$webStorageType.getItem("$key");
@ -74,12 +90,14 @@ class Storage {
return itemValue; return itemValue;
} }
///When passed a [key] name, will remove that key from the given Storage object if it exists.
Future<void> removeItem({@required String key}) async { Future<void> removeItem({@required String key}) async {
await _controller.evaluateJavascript(source: """ await _controller.evaluateJavascript(source: """
window.$webStorageType.removeItem("$key"); window.$webStorageType.removeItem("$key");
"""); """);
} }
///Returns the list of all items from the given Storage object.
Future<List<WebStorageItem>> getItems() async { Future<List<WebStorageItem>> getItems() async {
var webStorageItems = <WebStorageItem>[]; var webStorageItems = <WebStorageItem>[];
@ -112,12 +130,15 @@ class Storage {
return webStorageItems; return webStorageItems;
} }
///Clears all keys stored in a given Storage object.
Future<void> clear() async { Future<void> clear() async {
await _controller.evaluateJavascript(source: """ await _controller.evaluateJavascript(source: """
window.$webStorageType.clear(); window.$webStorageType.clear();
"""); """);
} }
///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: """ var result = await _controller.evaluateJavascript(source: """
window.$webStorageType.key($index); window.$webStorageType.key($index);
@ -126,11 +147,15 @@ class Storage {
} }
} }
///Class that provides methods to manage the JavaScript `window.localStorage` object.
///It used by [WebStorage].
class LocalStorage extends Storage { class LocalStorage extends Storage {
LocalStorage(InAppWebViewController controller) LocalStorage(InAppWebViewController controller)
: super(controller, WebStorageType.LOCAL_STORAGE); : super(controller, WebStorageType.LOCAL_STORAGE);
} }
///Class that provides methods to manage the JavaScript `window.sessionStorage` object.
///It used by [WebStorage].
class SessionStorage extends Storage { class SessionStorage extends Storage {
SessionStorage(InAppWebViewController controller) SessionStorage(InAppWebViewController controller)
: super(controller, WebStorageType.SESSION_STORAGE); : super(controller, WebStorageType.SESSION_STORAGE);

View File

@ -1,4 +1,4 @@
import 'package:flutter_inappwebview/src/context_menu.dart'; import 'context_menu.dart';
import 'types.dart'; import 'types.dart';
import 'in_app_webview_controller.dart'; import 'in_app_webview_controller.dart';
@ -365,7 +365,7 @@ abstract class WebView {
///**NOTE**: available only on Android. ///**NOTE**: available only on Android.
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onGeolocationPermissionsHidePrompt() ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onGeolocationPermissionsHidePrompt()
final Future<void> Function(InAppWebViewController controller) final void Function(InAppWebViewController controller)
androidOnGeolocationPermissionsHidePrompt; androidOnGeolocationPermissionsHidePrompt;
///Notify the host application of a resource request and allow the application to return the data. ///Notify the host application of a resource request and allow the application to return the data.

View File

@ -1,6 +1,6 @@
name: flutter_inappwebview name: flutter_inappwebview
description: A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window. description: A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window.
version: 3.4.0 version: 3.4.0+1
homepage: https://github.com/pichillilorenzo/flutter_inappwebview homepage: https://github.com/pichillilorenzo/flutter_inappwebview
environment: environment: