From b99824ee2172cd5bffbc05326e1c5705f50e63f5 Mon Sep 17 00:00:00 2001 From: Lorenzo Pichilli Date: Mon, 27 Nov 2023 12:09:17 +0100 Subject: [PATCH] created flutter_inappwebview_web, updated web_support.js path --- CHANGELOG.md | 5 +- README.md | 2 +- example/web/index.html | 2 +- .../headless_in_app_webview.dart | 2 +- .../src/in_app_webview/in_app_webview.dart | 8 +- .../lib/src/inappwebview_platform.dart | 2 +- .../headless_in_app_webview.dart | 2 +- .../src/in_app_webview/in_app_webview.dart | 8 +- .../headless_in_app_webview.dart | 2 +- .../src/in_app_webview/in_app_webview.dart | 8 +- .../platform_find_interaction_controller.dart | 2 +- flutter_inappwebview_web/.gitignore | 30 + flutter_inappwebview_web/.metadata | 30 + flutter_inappwebview_web/CHANGELOG.md | 3 + flutter_inappwebview_web/LICENSE | 1 + flutter_inappwebview_web/README.md | 15 + .../analysis_options.yaml | 13 + flutter_inappwebview_web/build.yaml | 5 + flutter_inappwebview_web/example/.gitignore | 44 + flutter_inappwebview_web/example/README.md | 16 + .../example/analysis_options.yaml | 28 + .../plugin_integration_test.dart | 0 .../example/lib/main.dart | 0 flutter_inappwebview_web/example/pubspec.lock | 295 ++++++ flutter_inappwebview_web/example/pubspec.yaml | 85 ++ .../example/test/widget_test.dart | 0 .../example/web/favicon.png | Bin 0 -> 917 bytes .../example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../example/web/icons/Icon-maskable-192.png | Bin 0 -> 5594 bytes .../example/web/icons/Icon-maskable-512.png | Bin 0 -> 20998 bytes .../example/web/index.html | 59 ++ .../example/web/manifest.json | 35 + .../lib}/assets/web/web_support.js | 9 + .../lib/flutter_inappwebview_web.dart | 4 + .../lib/src/cookie_manager.dart | 309 +++++++ .../src/in_app_webview/_static_channel.dart | 4 + .../headless_in_app_webview.dart | 428 +++++++++ .../src/in_app_webview/in_app_webview.dart | 384 ++++++++ .../in_app_webview_controller.dart | 855 ++++++++++++++++++ .../lib/src/in_app_webview/main.dart | 3 + .../lib/src/inappwebview_platform.dart | 65 ++ flutter_inappwebview_web/lib/src/main.dart | 4 + .../lib/src/platform_util.dart | 50 + .../lib/src/web_storage/main.dart | 1 + .../lib/src/web_storage/web_storage.dart | 257 ++++++ .../headless_in_app_web_view_web_element.dart | 2 +- .../web/headless_inappwebview_manager.dart | 4 +- .../lib}/web/in_app_web_view_web_element.dart | 11 +- .../lib}/web/main.dart | 0 .../lib}/web/platform_util.dart | 2 +- .../lib}/web/web_platform.dart | 14 +- .../lib}/web/web_platform_manager.dart | 0 flutter_inappwebview_web/pubspec.yaml | 85 ++ .../test/flutter_inappwebview_web_test.dart | 0 lib/flutter_inappwebview.dart | 1 - lib/src/web/main_stub.dart | 4 - lib/src/web/shims/dart_ui.dart | 1 - lib/src/web/shims/dart_ui_fake.dart | 29 - lib/src/web/shims/dart_ui_real.dart | 1 - pubspec.yaml | 9 +- 61 files changed, 3157 insertions(+), 81 deletions(-) create mode 100644 flutter_inappwebview_web/.gitignore create mode 100644 flutter_inappwebview_web/.metadata create mode 100644 flutter_inappwebview_web/CHANGELOG.md create mode 100644 flutter_inappwebview_web/LICENSE create mode 100644 flutter_inappwebview_web/README.md create mode 100644 flutter_inappwebview_web/analysis_options.yaml create mode 100644 flutter_inappwebview_web/build.yaml create mode 100644 flutter_inappwebview_web/example/.gitignore create mode 100644 flutter_inappwebview_web/example/README.md create mode 100644 flutter_inappwebview_web/example/analysis_options.yaml create mode 100644 flutter_inappwebview_web/example/integration_test/plugin_integration_test.dart create mode 100644 flutter_inappwebview_web/example/lib/main.dart create mode 100644 flutter_inappwebview_web/example/pubspec.lock create mode 100644 flutter_inappwebview_web/example/pubspec.yaml create mode 100644 flutter_inappwebview_web/example/test/widget_test.dart create mode 100644 flutter_inappwebview_web/example/web/favicon.png create mode 100644 flutter_inappwebview_web/example/web/icons/Icon-192.png create mode 100644 flutter_inappwebview_web/example/web/icons/Icon-512.png create mode 100644 flutter_inappwebview_web/example/web/icons/Icon-maskable-192.png create mode 100644 flutter_inappwebview_web/example/web/icons/Icon-maskable-512.png create mode 100644 flutter_inappwebview_web/example/web/index.html create mode 100644 flutter_inappwebview_web/example/web/manifest.json rename {lib => flutter_inappwebview_web/lib}/assets/web/web_support.js (98%) create mode 100644 flutter_inappwebview_web/lib/flutter_inappwebview_web.dart create mode 100755 flutter_inappwebview_web/lib/src/cookie_manager.dart create mode 100644 flutter_inappwebview_web/lib/src/in_app_webview/_static_channel.dart create mode 100644 flutter_inappwebview_web/lib/src/in_app_webview/headless_in_app_webview.dart create mode 100755 flutter_inappwebview_web/lib/src/in_app_webview/in_app_webview.dart create mode 100644 flutter_inappwebview_web/lib/src/in_app_webview/in_app_webview_controller.dart create mode 100644 flutter_inappwebview_web/lib/src/in_app_webview/main.dart create mode 100644 flutter_inappwebview_web/lib/src/inappwebview_platform.dart create mode 100644 flutter_inappwebview_web/lib/src/main.dart create mode 100644 flutter_inappwebview_web/lib/src/platform_util.dart create mode 100644 flutter_inappwebview_web/lib/src/web_storage/main.dart create mode 100644 flutter_inappwebview_web/lib/src/web_storage/web_storage.dart rename {lib/src => flutter_inappwebview_web/lib}/web/headless_in_app_web_view_web_element.dart (94%) rename {lib/src => flutter_inappwebview_web/lib}/web/headless_inappwebview_manager.dart (96%) rename {lib/src => flutter_inappwebview_web/lib}/web/in_app_web_view_web_element.dart (98%) rename {lib/src => flutter_inappwebview_web/lib}/web/main.dart (100%) rename {lib/src => flutter_inappwebview_web/lib}/web/platform_util.dart (92%) rename {lib/src => flutter_inappwebview_web/lib}/web/web_platform.dart (91%) rename {lib/src => flutter_inappwebview_web/lib}/web/web_platform_manager.dart (100%) create mode 100644 flutter_inappwebview_web/pubspec.yaml create mode 100644 flutter_inappwebview_web/test/flutter_inappwebview_web_test.dart delete mode 100644 lib/src/web/main_stub.dart delete mode 100644 lib/src/web/shims/dart_ui.dart delete mode 100644 lib/src/web/shims/dart_ui_fake.dart delete mode 100644 lib/src/web/shims/dart_ui_real.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 78122685..50f9f425 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,9 @@ -## 6.1.0-beta.1 +## 6.0.0-beta.29 ### BREAKING CHANGES -Plugin conversion to a [Federated Plugin](https://docs.flutter.dev/packages-and-plugins/developing-packages#federated-plugins) to better support multiple environments and implementations. +- Plugin conversion to a [Federated Plugin](https://docs.flutter.dev/packages-and-plugins/developing-packages#federated-plugins) to better support multiple environments and implementations. +- `web_support.js` path has been changed to `packages/flutter_inappwebview_web/assets/web/web_support.js` ## 6.0.0-beta.28 diff --git a/README.md b/README.md index 27cd8b0e..d9806055 100755 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ To make it work properly on the Web platform, you need to add the `web_support.j ```html - + ``` diff --git a/example/web/index.html b/example/web/index.html index f63aa99b..cb50c162 100644 --- a/example/web/index.html +++ b/example/web/index.html @@ -33,7 +33,7 @@ - + + + + + + + + + + + + + + + + + flutter_inappwebview_web_example + + + + + + + + + + diff --git a/flutter_inappwebview_web/example/web/manifest.json b/flutter_inappwebview_web/example/web/manifest.json new file mode 100644 index 00000000..62ee1511 --- /dev/null +++ b/flutter_inappwebview_web/example/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "flutter_inappwebview_web_example", + "short_name": "flutter_inappwebview_web_example", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "Demonstrates how to use the flutter_inappwebview_web plugin.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/lib/assets/web/web_support.js b/flutter_inappwebview_web/lib/assets/web/web_support.js similarity index 98% rename from lib/assets/web/web_support.js rename to flutter_inappwebview_web/lib/assets/web/web_support.js index 0cac0f45..031222d0 100644 --- a/lib/assets/web/web_support.js +++ b/flutter_inappwebview_web/lib/assets/web/web_support.js @@ -485,6 +485,15 @@ window.flutter_inappwebview = { } return null; }, + getContentWidth: function() { + var iframe = webView.iframe; + try { + return iframe.contentDocument.documentElement.scrollWidth; + } catch (e) { + console.log(e); + } + return null; + }, getSelectedText: function() { var iframe = webView.iframe; try { diff --git a/flutter_inappwebview_web/lib/flutter_inappwebview_web.dart b/flutter_inappwebview_web/lib/flutter_inappwebview_web.dart new file mode 100644 index 00000000..5886c476 --- /dev/null +++ b/flutter_inappwebview_web/lib/flutter_inappwebview_web.dart @@ -0,0 +1,4 @@ +library flutter_inappwebview_web; + +export 'src/main.dart'; +export 'web/main.dart'; diff --git a/flutter_inappwebview_web/lib/src/cookie_manager.dart b/flutter_inappwebview_web/lib/src/cookie_manager.dart new file mode 100755 index 00000000..92e6735b --- /dev/null +++ b/flutter_inappwebview_web/lib/src/cookie_manager.dart @@ -0,0 +1,309 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +import 'in_app_webview/headless_in_app_webview.dart'; +import 'platform_util.dart'; + +/// Object specifying creation parameters for creating a [WebPlatformCookieManager]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformCookieManagerCreationParams] for +/// more information. +@immutable +class WebPlatformCookieManagerCreationParams + extends PlatformCookieManagerCreationParams { + /// Creates a new [WebPlatformCookieManagerCreationParams] instance. + const WebPlatformCookieManagerCreationParams( + // This parameter prevents breaking changes later. + // ignore: avoid_unused_constructor_parameters + PlatformCookieManagerCreationParams params, + ) : super(); + + /// Creates a [WebPlatformCookieManagerCreationParams] instance based on [PlatformCookieManagerCreationParams]. + factory WebPlatformCookieManagerCreationParams.fromPlatformCookieManagerCreationParams( + PlatformCookieManagerCreationParams params) { + return WebPlatformCookieManagerCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformCookieManager} +class WebPlatformCookieManager extends PlatformCookieManager + with ChannelController { + /// Creates a new [WebPlatformCookieManager]. + WebPlatformCookieManager(PlatformCookieManagerCreationParams params) + : super.implementation( + params is WebPlatformCookieManagerCreationParams + ? params + : WebPlatformCookieManagerCreationParams + .fromPlatformCookieManagerCreationParams(params), + ) { + channel = const MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_cookiemanager'); + handler = handleMethod; + initMethodCallHandler(); + } + + static WebPlatformCookieManager? _instance; + + ///Gets the [WebPlatformCookieManager] shared instance. + static WebPlatformCookieManager instance() { + return (_instance != null) ? _instance! : _init(); + } + + static WebPlatformCookieManager _init() { + _instance = WebPlatformCookieManager(WebPlatformCookieManagerCreationParams( + const PlatformCookieManagerCreationParams())); + return _instance!; + } + + Future _handleMethod(MethodCall call) async {} + + @override + Future setCookie( + {required WebUri url, + required String name, + required String value, + String path = "/", + String? domain, + int? expiresDate, + int? maxAge, + bool? isSecure, + bool? isHttpOnly, + HTTPCookieSameSitePolicy? sameSite, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { + webViewController = webViewController ?? iosBelow11WebViewController; + + assert(url.toString().isNotEmpty); + assert(name.isNotEmpty); + assert(value.isNotEmpty); + assert(path.isNotEmpty); + + await _setCookieWithJavaScript( + url: url, + name: name, + value: value, + domain: domain, + path: path, + expiresDate: expiresDate, + maxAge: maxAge, + isSecure: isSecure, + sameSite: sameSite, + webViewController: webViewController); + return true; + } + + Future _setCookieWithJavaScript( + {required WebUri url, + required String name, + required String value, + String path = "/", + String? domain, + int? expiresDate, + int? maxAge, + bool? isSecure, + HTTPCookieSameSitePolicy? sameSite, + PlatformInAppWebViewController? webViewController}) async { + var cookieValue = name + "=" + value + "; Path=" + path; + + if (domain != null) cookieValue += "; Domain=" + domain; + + if (expiresDate != null) + cookieValue += "; Expires=" + await _getCookieExpirationDate(expiresDate); + + if (maxAge != null) cookieValue += "; Max-Age=" + maxAge.toString(); + + if (isSecure != null && isSecure) cookieValue += "; Secure"; + + if (sameSite != null) + cookieValue += "; SameSite=" + sameSite.toNativeValue(); + + cookieValue += ";"; + + if (webViewController != null) { + final javaScriptEnabled = + (await webViewController.getSettings())?.javaScriptEnabled ?? false; + if (javaScriptEnabled) { + await webViewController.evaluateJavascript( + source: 'document.cookie="$cookieValue"'); + return; + } + } + + final setCookieCompleter = Completer(); + final headlessWebView = + WebPlatformHeadlessInAppWebView(WebPlatformHeadlessInAppWebViewCreationParams( + initialUrlRequest: URLRequest(url: url), + onLoadStop: (controller, url) async { + await controller.evaluateJavascript( + source: 'document.cookie="$cookieValue"'); + setCookieCompleter.complete(); + })); + await headlessWebView.run(); + await setCookieCompleter.future; + await headlessWebView.dispose(); + } + + @override + Future> getCookies( + {required WebUri url, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { + assert(url.toString().isNotEmpty); + + webViewController = webViewController ?? iosBelow11WebViewController; + + return await _getCookiesWithJavaScript( + url: url, webViewController: webViewController); + } + + Future> _getCookiesWithJavaScript( + {required WebUri url, + PlatformInAppWebViewController? webViewController}) async { + assert(url.toString().isNotEmpty); + + List cookies = []; + + if (webViewController != null) { + final javaScriptEnabled = + (await webViewController.getSettings())?.javaScriptEnabled ?? false; + if (javaScriptEnabled) { + List documentCookies = (await webViewController + .evaluateJavascript(source: 'document.cookie') as String) + .split(';') + .map((documentCookie) => documentCookie.trim()) + .toList(); + documentCookies.forEach((documentCookie) { + List cookie = documentCookie.split('='); + if (cookie.length > 1) { + cookies.add(Cookie( + name: cookie[0], + value: cookie[1], + )); + } + }); + return cookies; + } + } + + final pageLoaded = Completer(); + final headlessWebView = + WebPlatformHeadlessInAppWebView(WebPlatformHeadlessInAppWebViewCreationParams( + initialUrlRequest: URLRequest(url: url), + onLoadStop: (controller, url) async { + pageLoaded.complete(); + }, + )); + await headlessWebView.run(); + await pageLoaded.future; + + List documentCookies = (await headlessWebView.webViewController! + .evaluateJavascript(source: 'document.cookie') as String) + .split(';') + .map((documentCookie) => documentCookie.trim()) + .toList(); + documentCookies.forEach((documentCookie) { + List cookie = documentCookie.split('='); + if (cookie.length > 1) { + cookies.add(Cookie( + name: cookie[0], + value: cookie[1], + )); + } + }); + await headlessWebView.dispose(); + return cookies; + } + + @override + Future getCookie( + {required WebUri url, + required String name, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { + assert(url.toString().isNotEmpty); + assert(name.isNotEmpty); + + webViewController = webViewController ?? iosBelow11WebViewController; + + List cookies = await _getCookiesWithJavaScript( + url: url, webViewController: webViewController); + return cookies + .cast() + .firstWhere((cookie) => cookie!.name == name, orElse: () => null); + } + + @override + Future deleteCookie( + {required WebUri url, + required String name, + String path = "/", + String? domain, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { + assert(url.toString().isNotEmpty); + assert(name.isNotEmpty); + + webViewController = webViewController ?? iosBelow11WebViewController; + + await _setCookieWithJavaScript( + url: url, + name: name, + value: "", + path: path, + domain: domain, + maxAge: -1, + webViewController: webViewController); + return; + } + + @override + Future deleteCookies( + {required WebUri url, + String path = "/", + String? domain, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { + assert(url.toString().isNotEmpty); + + webViewController = webViewController ?? iosBelow11WebViewController; + + List cookies = await _getCookiesWithJavaScript( + url: url, webViewController: webViewController); + for (var i = 0; i < cookies.length; i++) { + await _setCookieWithJavaScript( + url: url, + name: cookies[i].name, + value: "", + path: path, + domain: domain, + maxAge: -1, + webViewController: webViewController); + } + return; + } + + Future _getCookieExpirationDate(int expiresDate) async { + var platformUtil = PlatformUtil.instance(); + var dateTime = DateTime.fromMillisecondsSinceEpoch(expiresDate).toUtc(); + return await platformUtil.getWebCookieExpirationDate(date: dateTime); + } + + @override + void dispose() { + // empty + } +} + +extension InternalCookieManager on WebPlatformCookieManager { + get handleMethod => _handleMethod; +} diff --git a/flutter_inappwebview_web/lib/src/in_app_webview/_static_channel.dart b/flutter_inappwebview_web/lib/src/in_app_webview/_static_channel.dart new file mode 100644 index 00000000..beb7de70 --- /dev/null +++ b/flutter_inappwebview_web/lib/src/in_app_webview/_static_channel.dart @@ -0,0 +1,4 @@ +import 'package:flutter/services.dart'; + +const IN_APP_WEBVIEW_STATIC_CHANNEL = + MethodChannel('com.pichillilorenzo/flutter_inappwebview_manager'); diff --git a/flutter_inappwebview_web/lib/src/in_app_webview/headless_in_app_webview.dart b/flutter_inappwebview_web/lib/src/in_app_webview/headless_in_app_webview.dart new file mode 100644 index 00000000..77d50df6 --- /dev/null +++ b/flutter_inappwebview_web/lib/src/in_app_webview/headless_in_app_webview.dart @@ -0,0 +1,428 @@ +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; +import 'in_app_webview_controller.dart'; + +/// Object specifying creation parameters for creating a [WebPlatformHeadlessInAppWebView]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformHeadlessInAppWebViewCreationParams] for +/// more information. +@immutable +class WebPlatformHeadlessInAppWebViewCreationParams + extends PlatformHeadlessInAppWebViewCreationParams { + /// Creates a new [WebPlatformHeadlessInAppWebViewCreationParams] instance. + WebPlatformHeadlessInAppWebViewCreationParams( + {super.controllerFromPlatform, + super.initialSize, + super.windowId, + super.onWebViewCreated, + super.onLoadStart, + super.onLoadStop, + @Deprecated('Use onReceivedError instead') super.onLoadError, + super.onReceivedError, + @Deprecated("Use onReceivedHttpError instead") super.onLoadHttpError, + super.onReceivedHttpError, + super.onProgressChanged, + super.onConsoleMessage, + super.shouldOverrideUrlLoading, + super.onLoadResource, + super.onScrollChanged, + @Deprecated('Use onDownloadStartRequest instead') super.onDownloadStart, + super.onDownloadStartRequest, + @Deprecated('Use onLoadResourceWithCustomScheme instead') + super.onLoadResourceCustomScheme, + super.onLoadResourceWithCustomScheme, + super.onCreateWindow, + super.onCloseWindow, + super.onJsAlert, + super.onJsConfirm, + super.onJsPrompt, + super.onReceivedHttpAuthRequest, + super.onReceivedServerTrustAuthRequest, + super.onReceivedClientCertRequest, + @Deprecated('Use FindInteractionController.onFindResultReceived instead') + super.onFindResultReceived, + super.shouldInterceptAjaxRequest, + super.onAjaxReadyStateChange, + super.onAjaxProgress, + super.shouldInterceptFetchRequest, + super.onUpdateVisitedHistory, + @Deprecated("Use onPrintRequest instead") super.onPrint, + super.onPrintRequest, + super.onLongPressHitTestResult, + super.onEnterFullscreen, + super.onExitFullscreen, + super.onPageCommitVisible, + super.onTitleChanged, + super.onWindowFocus, + super.onWindowBlur, + super.onOverScrolled, + super.onZoomScaleChanged, + @Deprecated('Use onSafeBrowsingHit instead') + super.androidOnSafeBrowsingHit, + super.onSafeBrowsingHit, + @Deprecated('Use onPermissionRequest instead') + super.androidOnPermissionRequest, + super.onPermissionRequest, + @Deprecated('Use onGeolocationPermissionsShowPrompt instead') + super.androidOnGeolocationPermissionsShowPrompt, + super.onGeolocationPermissionsShowPrompt, + @Deprecated('Use onGeolocationPermissionsHidePrompt instead') + super.androidOnGeolocationPermissionsHidePrompt, + super.onGeolocationPermissionsHidePrompt, + @Deprecated('Use shouldInterceptRequest instead') + super.androidShouldInterceptRequest, + super.shouldInterceptRequest, + @Deprecated('Use onRenderProcessGone instead') + super.androidOnRenderProcessGone, + super.onRenderProcessGone, + @Deprecated('Use onRenderProcessResponsive instead') + super.androidOnRenderProcessResponsive, + super.onRenderProcessResponsive, + @Deprecated('Use onRenderProcessUnresponsive instead') + super.androidOnRenderProcessUnresponsive, + super.onRenderProcessUnresponsive, + @Deprecated('Use onFormResubmission instead') + super.androidOnFormResubmission, + super.onFormResubmission, + @Deprecated('Use onZoomScaleChanged instead') super.androidOnScaleChanged, + @Deprecated('Use onReceivedIcon instead') super.androidOnReceivedIcon, + super.onReceivedIcon, + @Deprecated('Use onReceivedTouchIconUrl instead') + super.androidOnReceivedTouchIconUrl, + super.onReceivedTouchIconUrl, + @Deprecated('Use onJsBeforeUnload instead') super.androidOnJsBeforeUnload, + super.onJsBeforeUnload, + @Deprecated('Use onReceivedLoginRequest instead') + super.androidOnReceivedLoginRequest, + super.onReceivedLoginRequest, + super.onPermissionRequestCanceled, + super.onRequestFocus, + @Deprecated('Use onWebContentProcessDidTerminate instead') + super.iosOnWebContentProcessDidTerminate, + super.onWebContentProcessDidTerminate, + @Deprecated( + 'Use onDidReceiveServerRedirectForProvisionalNavigation instead') + super.iosOnDidReceiveServerRedirectForProvisionalNavigation, + super.onDidReceiveServerRedirectForProvisionalNavigation, + @Deprecated('Use onNavigationResponse instead') + super.iosOnNavigationResponse, + super.onNavigationResponse, + @Deprecated('Use shouldAllowDeprecatedTLS instead') + super.iosShouldAllowDeprecatedTLS, + super.shouldAllowDeprecatedTLS, + super.onCameraCaptureStateChanged, + super.onMicrophoneCaptureStateChanged, + super.onContentSizeChanged, + super.initialUrlRequest, + super.initialFile, + super.initialData, + @Deprecated('Use initialSettings instead') super.initialOptions, + super.initialSettings, + super.contextMenu, + super.initialUserScripts, + super.pullToRefreshController, + super.findInteractionController}); + + /// Creates a [WebPlatformHeadlessInAppWebViewCreationParams] instance based on [PlatformHeadlessInAppWebViewCreationParams]. + WebPlatformHeadlessInAppWebViewCreationParams.fromPlatformHeadlessInAppWebViewCreationParams( + PlatformHeadlessInAppWebViewCreationParams params) + : this( + controllerFromPlatform: params.controllerFromPlatform, + initialSize: params.initialSize, + windowId: params.windowId, + onWebViewCreated: params.onWebViewCreated, + onLoadStart: params.onLoadStart, + onLoadStop: params.onLoadStop, + onLoadError: params.onLoadError, + onReceivedError: params.onReceivedError, + onLoadHttpError: params.onLoadHttpError, + onReceivedHttpError: params.onReceivedHttpError, + onProgressChanged: params.onProgressChanged, + onConsoleMessage: params.onConsoleMessage, + shouldOverrideUrlLoading: params.shouldOverrideUrlLoading, + onLoadResource: params.onLoadResource, + onScrollChanged: params.onScrollChanged, + onDownloadStart: params.onDownloadStart, + onDownloadStartRequest: params.onDownloadStartRequest, + onLoadResourceCustomScheme: params.onLoadResourceCustomScheme, + onLoadResourceWithCustomScheme: + params.onLoadResourceWithCustomScheme, + onCreateWindow: params.onCreateWindow, + onCloseWindow: params.onCloseWindow, + onJsAlert: params.onJsAlert, + onJsConfirm: params.onJsConfirm, + onJsPrompt: params.onJsPrompt, + onReceivedHttpAuthRequest: params.onReceivedHttpAuthRequest, + onReceivedServerTrustAuthRequest: + params.onReceivedServerTrustAuthRequest, + onReceivedClientCertRequest: params.onReceivedClientCertRequest, + onFindResultReceived: params.onFindResultReceived, + shouldInterceptAjaxRequest: params.shouldInterceptAjaxRequest, + onAjaxReadyStateChange: params.onAjaxReadyStateChange, + onAjaxProgress: params.onAjaxProgress, + shouldInterceptFetchRequest: params.shouldInterceptFetchRequest, + onUpdateVisitedHistory: params.onUpdateVisitedHistory, + onPrint: params.onPrint, + onPrintRequest: params.onPrintRequest, + onLongPressHitTestResult: params.onLongPressHitTestResult, + onEnterFullscreen: params.onEnterFullscreen, + onExitFullscreen: params.onExitFullscreen, + onPageCommitVisible: params.onPageCommitVisible, + onTitleChanged: params.onTitleChanged, + onWindowFocus: params.onWindowFocus, + onWindowBlur: params.onWindowBlur, + onOverScrolled: params.onOverScrolled, + onZoomScaleChanged: params.onZoomScaleChanged, + androidOnSafeBrowsingHit: params.androidOnSafeBrowsingHit, + onSafeBrowsingHit: params.onSafeBrowsingHit, + androidOnPermissionRequest: params.androidOnPermissionRequest, + onPermissionRequest: params.onPermissionRequest, + androidOnGeolocationPermissionsShowPrompt: + params.androidOnGeolocationPermissionsShowPrompt, + onGeolocationPermissionsShowPrompt: + params.onGeolocationPermissionsShowPrompt, + androidOnGeolocationPermissionsHidePrompt: + params.androidOnGeolocationPermissionsHidePrompt, + onGeolocationPermissionsHidePrompt: + params.onGeolocationPermissionsHidePrompt, + androidShouldInterceptRequest: params.androidShouldInterceptRequest, + shouldInterceptRequest: params.shouldInterceptRequest, + androidOnRenderProcessGone: params.androidOnRenderProcessGone, + onRenderProcessGone: params.onRenderProcessGone, + androidOnRenderProcessResponsive: + params.androidOnRenderProcessResponsive, + onRenderProcessResponsive: params.onRenderProcessResponsive, + androidOnRenderProcessUnresponsive: + params.androidOnRenderProcessUnresponsive, + onRenderProcessUnresponsive: params.onRenderProcessUnresponsive, + androidOnFormResubmission: params.androidOnFormResubmission, + onFormResubmission: params.onFormResubmission, + androidOnScaleChanged: params.androidOnScaleChanged, + androidOnReceivedIcon: params.androidOnReceivedIcon, + onReceivedIcon: params.onReceivedIcon, + androidOnReceivedTouchIconUrl: params.androidOnReceivedTouchIconUrl, + onReceivedTouchIconUrl: params.onReceivedTouchIconUrl, + androidOnJsBeforeUnload: params.androidOnJsBeforeUnload, + onJsBeforeUnload: params.onJsBeforeUnload, + androidOnReceivedLoginRequest: params.androidOnReceivedLoginRequest, + onReceivedLoginRequest: params.onReceivedLoginRequest, + onPermissionRequestCanceled: params.onPermissionRequestCanceled, + onRequestFocus: params.onRequestFocus, + iosOnWebContentProcessDidTerminate: + params.iosOnWebContentProcessDidTerminate, + onWebContentProcessDidTerminate: + params.onWebContentProcessDidTerminate, + iosOnDidReceiveServerRedirectForProvisionalNavigation: + params.iosOnDidReceiveServerRedirectForProvisionalNavigation, + onDidReceiveServerRedirectForProvisionalNavigation: + params.onDidReceiveServerRedirectForProvisionalNavigation, + iosOnNavigationResponse: params.iosOnNavigationResponse, + onNavigationResponse: params.onNavigationResponse, + iosShouldAllowDeprecatedTLS: params.iosShouldAllowDeprecatedTLS, + shouldAllowDeprecatedTLS: params.shouldAllowDeprecatedTLS, + onCameraCaptureStateChanged: params.onCameraCaptureStateChanged, + onMicrophoneCaptureStateChanged: + params.onMicrophoneCaptureStateChanged, + onContentSizeChanged: params.onContentSizeChanged, + initialUrlRequest: params.initialUrlRequest, + initialFile: params.initialFile, + initialData: params.initialData, + initialOptions: params.initialOptions, + initialSettings: params.initialSettings, + contextMenu: params.contextMenu, + initialUserScripts: params.initialUserScripts, + pullToRefreshController: params.pullToRefreshController, + findInteractionController: params.findInteractionController); +} + +///{@macro flutter_inappwebview_platform_interface.PlatformHeadlessInAppWebView} +class WebPlatformHeadlessInAppWebView extends PlatformHeadlessInAppWebView + with ChannelController { + @override + late final String id; + + bool _started = false; + bool _running = false; + + static const MethodChannel _sharedChannel = + const MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview'); + + WebPlatformInAppWebViewController? _webViewController; + + /// Constructs a [WebPlatformHeadlessInAppWebView]. + WebPlatformHeadlessInAppWebView(PlatformHeadlessInAppWebViewCreationParams params) + : super.implementation( + params is WebPlatformHeadlessInAppWebViewCreationParams + ? params + : WebPlatformHeadlessInAppWebViewCreationParams + .fromPlatformHeadlessInAppWebViewCreationParams(params), + ) { + id = IdGenerator.generate(); + } + + @override + WebPlatformInAppWebViewController? get webViewController => _webViewController; + + dynamic _controllerFromPlatform; + + WebPlatformHeadlessInAppWebViewCreationParams get _macosParams => + params as WebPlatformHeadlessInAppWebViewCreationParams; + + _init() { + _webViewController = WebPlatformInAppWebViewController( + WebPlatformInAppWebViewControllerCreationParams( + id: id, webviewParams: params), + ); + _controllerFromPlatform = + params.controllerFromPlatform?.call(_webViewController!) ?? + _webViewController!; + channel = + MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview_$id'); + handler = _handleMethod; + initMethodCallHandler(); + } + + Future _handleMethod(MethodCall call) async { + switch (call.method) { + case "onWebViewCreated": + if (params.onWebViewCreated != null && _webViewController != null) { + params.onWebViewCreated!(_controllerFromPlatform); + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + return null; + } + + Future run() async { + if (_started) { + return; + } + _started = true; + _init(); + + final initialSettings = params.initialSettings ?? InAppWebViewSettings(); + _inferInitialSettings(initialSettings); + + Map settingsMap = + (params.initialSettings != null ? initialSettings.toMap() : null) ?? + params.initialOptions?.toMap() ?? + initialSettings.toMap(); + + Map pullToRefreshSettings = + _macosParams.pullToRefreshController?.params.settings.toMap() ?? + _macosParams.pullToRefreshController?.params.options.toMap() ?? + PullToRefreshSettings(enabled: false).toMap(); + + Map args = {}; + args.putIfAbsent('id', () => id); + args.putIfAbsent( + 'params', + () => { + 'initialUrlRequest': params.initialUrlRequest?.toMap(), + 'initialFile': params.initialFile, + 'initialData': params.initialData?.toMap(), + 'initialSettings': settingsMap, + 'contextMenu': params.contextMenu?.toMap() ?? {}, + 'windowId': params.windowId, + 'initialUserScripts': + params.initialUserScripts?.map((e) => e.toMap()).toList() ?? + [], + 'pullToRefreshSettings': pullToRefreshSettings, + 'initialSize': params.initialSize.toMap() + }); + await _sharedChannel.invokeMethod('run', args); + _running = true; + } + + void _inferInitialSettings(InAppWebViewSettings settings) { + if (params.shouldOverrideUrlLoading != null && + settings.useShouldOverrideUrlLoading == null) { + settings.useShouldOverrideUrlLoading = true; + } + if (params.onLoadResource != null && settings.useOnLoadResource == null) { + settings.useOnLoadResource = true; + } + if (params.onDownloadStartRequest != null && + settings.useOnDownloadStart == null) { + settings.useOnDownloadStart = true; + } + if (params.shouldInterceptAjaxRequest != null && + settings.useShouldInterceptAjaxRequest == null) { + settings.useShouldInterceptAjaxRequest = true; + } + if (params.shouldInterceptFetchRequest != null && + settings.useShouldInterceptFetchRequest == null) { + settings.useShouldInterceptFetchRequest = true; + } + if (params.shouldInterceptRequest != null && + settings.useShouldInterceptRequest == null) { + settings.useShouldInterceptRequest = true; + } + if (params.onRenderProcessGone != null && + settings.useOnRenderProcessGone == null) { + settings.useOnRenderProcessGone = true; + } + if (params.onNavigationResponse != null && + settings.useOnNavigationResponse == null) { + settings.useOnNavigationResponse = true; + } + } + + @override + bool isRunning() { + return _running; + } + + @override + Future setSize(Size size) async { + if (!_running) { + return; + } + + Map args = {}; + args.putIfAbsent('size', () => size.toMap()); + await channel?.invokeMethod('setSize', args); + } + + @override + Future getSize() async { + if (!_running) { + return null; + } + + Map args = {}; + Map sizeMap = + (await channel?.invokeMethod('getSize', args))?.cast(); + return MapSize.fromMap(sizeMap); + } + + @override + Future dispose() async { + if (!_running) { + return; + } + Map args = {}; + await channel?.invokeMethod('dispose', args); + disposeChannel(); + _started = false; + _running = false; + _webViewController?.dispose(); + _webViewController = null; + _controllerFromPlatform = null; + _macosParams.pullToRefreshController?.dispose(); + _macosParams.findInteractionController?.dispose(); + } +} + +extension InternalHeadlessInAppWebView on WebPlatformHeadlessInAppWebView { + Future internalDispose() async { + _started = false; + _running = false; + } +} diff --git a/flutter_inappwebview_web/lib/src/in_app_webview/in_app_webview.dart b/flutter_inappwebview_web/lib/src/in_app_webview/in_app_webview.dart new file mode 100755 index 00000000..6adbd000 --- /dev/null +++ b/flutter_inappwebview_web/lib/src/in_app_webview/in_app_webview.dart @@ -0,0 +1,384 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; +import '../../web/web_platform_manager.dart'; +import 'headless_in_app_webview.dart'; + +import 'in_app_webview_controller.dart'; + +/// Object specifying creation parameters for creating a [PlatformInAppWebViewWidget]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +class WebPlatformInAppWebViewWidgetCreationParams + extends PlatformInAppWebViewWidgetCreationParams { + WebPlatformInAppWebViewWidgetCreationParams( + {super.controllerFromPlatform, + super.key, + super.layoutDirection, + super.gestureRecognizers, + super.headlessWebView, + super.keepAlive, + super.preventGestureDelay, + super.windowId, + super.onWebViewCreated, + super.onLoadStart, + super.onLoadStop, + @Deprecated('Use onReceivedError instead') super.onLoadError, + super.onReceivedError, + @Deprecated("Use onReceivedHttpError instead") super.onLoadHttpError, + super.onReceivedHttpError, + super.onProgressChanged, + super.onConsoleMessage, + super.shouldOverrideUrlLoading, + super.onLoadResource, + super.onScrollChanged, + @Deprecated('Use onDownloadStartRequest instead') super.onDownloadStart, + super.onDownloadStartRequest, + @Deprecated('Use onLoadResourceWithCustomScheme instead') + super.onLoadResourceCustomScheme, + super.onLoadResourceWithCustomScheme, + super.onCreateWindow, + super.onCloseWindow, + super.onJsAlert, + super.onJsConfirm, + super.onJsPrompt, + super.onReceivedHttpAuthRequest, + super.onReceivedServerTrustAuthRequest, + super.onReceivedClientCertRequest, + @Deprecated('Use FindInteractionController.onFindResultReceived instead') + super.onFindResultReceived, + super.shouldInterceptAjaxRequest, + super.onAjaxReadyStateChange, + super.onAjaxProgress, + super.shouldInterceptFetchRequest, + super.onUpdateVisitedHistory, + @Deprecated("Use onPrintRequest instead") super.onPrint, + super.onPrintRequest, + super.onLongPressHitTestResult, + super.onEnterFullscreen, + super.onExitFullscreen, + super.onPageCommitVisible, + super.onTitleChanged, + super.onWindowFocus, + super.onWindowBlur, + super.onOverScrolled, + super.onZoomScaleChanged, + @Deprecated('Use onSafeBrowsingHit instead') + super.androidOnSafeBrowsingHit, + super.onSafeBrowsingHit, + @Deprecated('Use onPermissionRequest instead') + super.androidOnPermissionRequest, + super.onPermissionRequest, + @Deprecated('Use onGeolocationPermissionsShowPrompt instead') + super.androidOnGeolocationPermissionsShowPrompt, + super.onGeolocationPermissionsShowPrompt, + @Deprecated('Use onGeolocationPermissionsHidePrompt instead') + super.androidOnGeolocationPermissionsHidePrompt, + super.onGeolocationPermissionsHidePrompt, + @Deprecated('Use shouldInterceptRequest instead') + super.androidShouldInterceptRequest, + super.shouldInterceptRequest, + @Deprecated('Use onRenderProcessGone instead') + super.androidOnRenderProcessGone, + super.onRenderProcessGone, + @Deprecated('Use onRenderProcessResponsive instead') + super.androidOnRenderProcessResponsive, + super.onRenderProcessResponsive, + @Deprecated('Use onRenderProcessUnresponsive instead') + super.androidOnRenderProcessUnresponsive, + super.onRenderProcessUnresponsive, + @Deprecated('Use onFormResubmission instead') + super.androidOnFormResubmission, + super.onFormResubmission, + @Deprecated('Use onZoomScaleChanged instead') super.androidOnScaleChanged, + @Deprecated('Use onReceivedIcon instead') super.androidOnReceivedIcon, + super.onReceivedIcon, + @Deprecated('Use onReceivedTouchIconUrl instead') + super.androidOnReceivedTouchIconUrl, + super.onReceivedTouchIconUrl, + @Deprecated('Use onJsBeforeUnload instead') super.androidOnJsBeforeUnload, + super.onJsBeforeUnload, + @Deprecated('Use onReceivedLoginRequest instead') + super.androidOnReceivedLoginRequest, + super.onReceivedLoginRequest, + super.onPermissionRequestCanceled, + super.onRequestFocus, + @Deprecated('Use onWebContentProcessDidTerminate instead') + super.iosOnWebContentProcessDidTerminate, + super.onWebContentProcessDidTerminate, + @Deprecated( + 'Use onDidReceiveServerRedirectForProvisionalNavigation instead') + super.iosOnDidReceiveServerRedirectForProvisionalNavigation, + super.onDidReceiveServerRedirectForProvisionalNavigation, + @Deprecated('Use onNavigationResponse instead') + super.iosOnNavigationResponse, + super.onNavigationResponse, + @Deprecated('Use shouldAllowDeprecatedTLS instead') + super.iosShouldAllowDeprecatedTLS, + super.shouldAllowDeprecatedTLS, + super.onCameraCaptureStateChanged, + super.onMicrophoneCaptureStateChanged, + super.onContentSizeChanged, + super.initialUrlRequest, + super.initialFile, + super.initialData, + @Deprecated('Use initialSettings instead') super.initialOptions, + super.initialSettings, + super.contextMenu, + super.initialUserScripts, + super.pullToRefreshController, + super.findInteractionController}); + + /// Constructs a [WebPlatformInAppWebViewWidgetCreationParams] using a + /// [PlatformInAppWebViewWidgetCreationParams]. + WebPlatformInAppWebViewWidgetCreationParams.fromPlatformInAppWebViewWidgetCreationParams( + PlatformInAppWebViewWidgetCreationParams params) + : this( + controllerFromPlatform: params.controllerFromPlatform, + key: params.key, + layoutDirection: params.layoutDirection, + gestureRecognizers: params.gestureRecognizers, + headlessWebView: params.headlessWebView, + keepAlive: params.keepAlive, + preventGestureDelay: params.preventGestureDelay, + windowId: params.windowId, + onWebViewCreated: params.onWebViewCreated, + onLoadStart: params.onLoadStart, + onLoadStop: params.onLoadStop, + onLoadError: params.onLoadError, + onReceivedError: params.onReceivedError, + onLoadHttpError: params.onLoadHttpError, + onReceivedHttpError: params.onReceivedHttpError, + onProgressChanged: params.onProgressChanged, + onConsoleMessage: params.onConsoleMessage, + shouldOverrideUrlLoading: params.shouldOverrideUrlLoading, + onLoadResource: params.onLoadResource, + onScrollChanged: params.onScrollChanged, + onDownloadStart: params.onDownloadStart, + onDownloadStartRequest: params.onDownloadStartRequest, + onLoadResourceCustomScheme: params.onLoadResourceCustomScheme, + onLoadResourceWithCustomScheme: + params.onLoadResourceWithCustomScheme, + onCreateWindow: params.onCreateWindow, + onCloseWindow: params.onCloseWindow, + onJsAlert: params.onJsAlert, + onJsConfirm: params.onJsConfirm, + onJsPrompt: params.onJsPrompt, + onReceivedHttpAuthRequest: params.onReceivedHttpAuthRequest, + onReceivedServerTrustAuthRequest: + params.onReceivedServerTrustAuthRequest, + onReceivedClientCertRequest: params.onReceivedClientCertRequest, + onFindResultReceived: params.onFindResultReceived, + shouldInterceptAjaxRequest: params.shouldInterceptAjaxRequest, + onAjaxReadyStateChange: params.onAjaxReadyStateChange, + onAjaxProgress: params.onAjaxProgress, + shouldInterceptFetchRequest: params.shouldInterceptFetchRequest, + onUpdateVisitedHistory: params.onUpdateVisitedHistory, + onPrint: params.onPrint, + onPrintRequest: params.onPrintRequest, + onLongPressHitTestResult: params.onLongPressHitTestResult, + onEnterFullscreen: params.onEnterFullscreen, + onExitFullscreen: params.onExitFullscreen, + onPageCommitVisible: params.onPageCommitVisible, + onTitleChanged: params.onTitleChanged, + onWindowFocus: params.onWindowFocus, + onWindowBlur: params.onWindowBlur, + onOverScrolled: params.onOverScrolled, + onZoomScaleChanged: params.onZoomScaleChanged, + androidOnSafeBrowsingHit: params.androidOnSafeBrowsingHit, + onSafeBrowsingHit: params.onSafeBrowsingHit, + androidOnPermissionRequest: params.androidOnPermissionRequest, + onPermissionRequest: params.onPermissionRequest, + androidOnGeolocationPermissionsShowPrompt: + params.androidOnGeolocationPermissionsShowPrompt, + onGeolocationPermissionsShowPrompt: + params.onGeolocationPermissionsShowPrompt, + androidOnGeolocationPermissionsHidePrompt: + params.androidOnGeolocationPermissionsHidePrompt, + onGeolocationPermissionsHidePrompt: + params.onGeolocationPermissionsHidePrompt, + androidShouldInterceptRequest: params.androidShouldInterceptRequest, + shouldInterceptRequest: params.shouldInterceptRequest, + androidOnRenderProcessGone: params.androidOnRenderProcessGone, + onRenderProcessGone: params.onRenderProcessGone, + androidOnRenderProcessResponsive: + params.androidOnRenderProcessResponsive, + onRenderProcessResponsive: params.onRenderProcessResponsive, + androidOnRenderProcessUnresponsive: + params.androidOnRenderProcessUnresponsive, + onRenderProcessUnresponsive: params.onRenderProcessUnresponsive, + androidOnFormResubmission: params.androidOnFormResubmission, + onFormResubmission: params.onFormResubmission, + androidOnScaleChanged: params.androidOnScaleChanged, + androidOnReceivedIcon: params.androidOnReceivedIcon, + onReceivedIcon: params.onReceivedIcon, + androidOnReceivedTouchIconUrl: params.androidOnReceivedTouchIconUrl, + onReceivedTouchIconUrl: params.onReceivedTouchIconUrl, + androidOnJsBeforeUnload: params.androidOnJsBeforeUnload, + onJsBeforeUnload: params.onJsBeforeUnload, + androidOnReceivedLoginRequest: params.androidOnReceivedLoginRequest, + onReceivedLoginRequest: params.onReceivedLoginRequest, + onPermissionRequestCanceled: params.onPermissionRequestCanceled, + onRequestFocus: params.onRequestFocus, + iosOnWebContentProcessDidTerminate: + params.iosOnWebContentProcessDidTerminate, + onWebContentProcessDidTerminate: + params.onWebContentProcessDidTerminate, + iosOnDidReceiveServerRedirectForProvisionalNavigation: + params.iosOnDidReceiveServerRedirectForProvisionalNavigation, + onDidReceiveServerRedirectForProvisionalNavigation: + params.onDidReceiveServerRedirectForProvisionalNavigation, + iosOnNavigationResponse: params.iosOnNavigationResponse, + onNavigationResponse: params.onNavigationResponse, + iosShouldAllowDeprecatedTLS: params.iosShouldAllowDeprecatedTLS, + shouldAllowDeprecatedTLS: params.shouldAllowDeprecatedTLS, + onCameraCaptureStateChanged: params.onCameraCaptureStateChanged, + onMicrophoneCaptureStateChanged: + params.onMicrophoneCaptureStateChanged, + onContentSizeChanged: params.onContentSizeChanged, + initialUrlRequest: params.initialUrlRequest, + initialFile: params.initialFile, + initialData: params.initialData, + initialOptions: params.initialOptions, + initialSettings: params.initialSettings, + contextMenu: params.contextMenu, + initialUserScripts: params.initialUserScripts, + pullToRefreshController: params.pullToRefreshController, + findInteractionController: params.findInteractionController); +} + +///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewWidget} +class WebPlatformInAppWebViewWidget extends PlatformInAppWebViewWidget { + /// Constructs a [WebPlatformInAppWebViewWidget]. + /// + ///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewWidget} + WebPlatformInAppWebViewWidget(PlatformInAppWebViewWidgetCreationParams params) + : super.implementation( + params is WebPlatformInAppWebViewWidgetCreationParams + ? params + : WebPlatformInAppWebViewWidgetCreationParams + .fromPlatformInAppWebViewWidgetCreationParams(params), + ); + + WebPlatformInAppWebViewWidgetCreationParams get _webPlatformParams => + params as WebPlatformInAppWebViewWidgetCreationParams; + + WebPlatformInAppWebViewController? _controller; + + WebPlatformHeadlessInAppWebView? get _macosHeadlessInAppWebView => + _webPlatformParams.headlessWebView as WebPlatformHeadlessInAppWebView?; + + @override + Widget build(BuildContext context) { + final initialSettings = + _webPlatformParams.initialSettings ?? InAppWebViewSettings(); + _inferInitialSettings(initialSettings); + + if ((_webPlatformParams.headlessWebView?.isRunning() ?? false) && + _webPlatformParams.keepAlive != null) { + final headlessId = _webPlatformParams.headlessWebView?.id; + if (headlessId != null) { + // force keep alive id to match headless webview id + _webPlatformParams.keepAlive?.id = headlessId; + } + } + + return HtmlElementView( + viewType: 'com.pichillilorenzo/flutter_inappwebview', + onPlatformViewCreated: (int viewId) { + var webViewHtmlElement = WebPlatformManager.webViews[viewId]!; + webViewHtmlElement.initialSettings = initialSettings; + webViewHtmlElement.initialUrlRequest = _webPlatformParams.initialUrlRequest; + webViewHtmlElement.initialFile = _webPlatformParams.initialFile; + webViewHtmlElement.initialData = _webPlatformParams.initialData; + webViewHtmlElement.headlessWebViewId = + _webPlatformParams.headlessWebView?.isRunning() ?? false + ? _webPlatformParams.headlessWebView?.id + : null; + webViewHtmlElement.prepare(); + if (webViewHtmlElement.headlessWebViewId == null) { + webViewHtmlElement.makeInitialLoad(); + } + _onPlatformViewCreated(viewId); + }, + ); + } + + void _onPlatformViewCreated(int id) { + dynamic viewId = id; + _macosHeadlessInAppWebView?.internalDispose(); + _controller = WebPlatformInAppWebViewController( + PlatformInAppWebViewControllerCreationParams( + id: viewId, webviewParams: params)); + debugLog( + className: runtimeType.toString(), + id: viewId?.toString(), + debugLoggingSettings: + PlatformInAppWebViewController.debugLoggingSettings, + method: "onWebViewCreated", + args: []); + if (_webPlatformParams.onWebViewCreated != null) { + _webPlatformParams.onWebViewCreated!( + params.controllerFromPlatform?.call(_controller!) ?? _controller!); + } + } + + void _inferInitialSettings(InAppWebViewSettings settings) { + if (_webPlatformParams.shouldOverrideUrlLoading != null && + settings.useShouldOverrideUrlLoading == null) { + settings.useShouldOverrideUrlLoading = true; + } + if (_webPlatformParams.onLoadResource != null && + settings.useOnLoadResource == null) { + settings.useOnLoadResource = true; + } + if (_webPlatformParams.onDownloadStartRequest != null && + settings.useOnDownloadStart == null) { + settings.useOnDownloadStart = true; + } + if (_webPlatformParams.shouldInterceptAjaxRequest != null && + settings.useShouldInterceptAjaxRequest == null) { + settings.useShouldInterceptAjaxRequest = true; + } + if (_webPlatformParams.shouldInterceptFetchRequest != null && + settings.useShouldInterceptFetchRequest == null) { + settings.useShouldInterceptFetchRequest = true; + } + if (_webPlatformParams.shouldInterceptRequest != null && + settings.useShouldInterceptRequest == null) { + settings.useShouldInterceptRequest = true; + } + if (_webPlatformParams.onRenderProcessGone != null && + settings.useOnRenderProcessGone == null) { + settings.useOnRenderProcessGone = true; + } + if (_webPlatformParams.onNavigationResponse != null && + settings.useOnNavigationResponse == null) { + settings.useOnNavigationResponse = true; + } + } + + @override + void dispose() { + dynamic viewId = _controller?.getViewId(); + debugLog( + className: runtimeType.toString(), + id: viewId?.toString(), + debugLoggingSettings: + PlatformInAppWebViewController.debugLoggingSettings, + method: "dispose", + args: []); + final isKeepAlive = _webPlatformParams.keepAlive != null; + _controller?.dispose(isKeepAlive: isKeepAlive); + _controller = null; + _webPlatformParams.pullToRefreshController?.dispose(isKeepAlive: isKeepAlive); + _webPlatformParams.findInteractionController?.dispose(isKeepAlive: isKeepAlive); + } + + @override + T controllerFromPlatform(PlatformInAppWebViewController controller) { + // unused + throw UnimplementedError(); + } +} diff --git a/flutter_inappwebview_web/lib/src/in_app_webview/in_app_webview_controller.dart b/flutter_inappwebview_web/lib/src/in_app_webview/in_app_webview_controller.dart new file mode 100644 index 00000000..685b1381 --- /dev/null +++ b/flutter_inappwebview_web/lib/src/in_app_webview/in_app_webview_controller.dart @@ -0,0 +1,855 @@ +import 'dart:convert'; +import 'dart:core'; +import 'dart:typed_data'; +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +import '../web_storage/web_storage.dart'; + +import 'headless_in_app_webview.dart'; +import '_static_channel.dart'; + +/// Object specifying creation parameters for creating a [WebPlatformInAppWebViewController]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformInAppWebViewControllerCreationParams] for +/// more information. +@immutable +class WebPlatformInAppWebViewControllerCreationParams + extends PlatformInAppWebViewControllerCreationParams { + /// Creates a new [WebPlatformInAppWebViewControllerCreationParams] instance. + const WebPlatformInAppWebViewControllerCreationParams( + {required super.id, super.webviewParams}); + + /// Creates a [WebPlatformInAppWebViewControllerCreationParams] instance based on [PlatformInAppWebViewControllerCreationParams]. + factory WebPlatformInAppWebViewControllerCreationParams.fromPlatformInAppWebViewControllerCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformInAppWebViewControllerCreationParams params) { + return WebPlatformInAppWebViewControllerCreationParams( + id: params.id, webviewParams: params.webviewParams); + } +} + +///Controls a WebView, such as an [InAppWebView] widget instance, a [WebPlatformHeadlessInAppWebView] instance or [WebPlatformInAppBrowser] WebView instance. +/// +///If you are using the [InAppWebView] widget, an [InAppWebViewController] instance can be obtained by setting the [InAppWebView.onWebViewCreated] +///callback. Instead, if you are using an [WebPlatformInAppBrowser] instance, you can get it through the [WebPlatformInAppBrowser.webViewController] attribute. +class WebPlatformInAppWebViewController extends PlatformInAppWebViewController + with ChannelController { + // ignore: unused_field + static final MethodChannel _staticChannel = IN_APP_WEBVIEW_STATIC_CHANNEL; + + Map _injectedScriptsFromURL = {}; + + dynamic _controllerFromPlatform; + + @override + late WebPlatformWebStorage webStorage; + + WebPlatformInAppWebViewController( + PlatformInAppWebViewControllerCreationParams params) + : super.implementation(params is WebPlatformInAppWebViewControllerCreationParams + ? params + : WebPlatformInAppWebViewControllerCreationParams + .fromPlatformInAppWebViewControllerCreationParams(params)) { + channel = MethodChannel('com.pichillilorenzo/flutter_inappwebview_$id'); + handler = handleMethod; + initMethodCallHandler(); + + this._init(params); + } + + static final WebPlatformInAppWebViewController _staticValue = + WebPlatformInAppWebViewController( + WebPlatformInAppWebViewControllerCreationParams(id: null)); + + factory WebPlatformInAppWebViewController.static() { + return _staticValue; + } + + void _init(PlatformInAppWebViewControllerCreationParams params) { + _controllerFromPlatform = + params.webviewParams?.controllerFromPlatform?.call(this) ?? this; + + webStorage = WebPlatformWebStorage(WebPlatformWebStorageCreationParams( + localStorage: WebPlatformLocalStorage.defaultStorage(controller: this), + sessionStorage: WebPlatformSessionStorage.defaultStorage(controller: this))); + } + + _debugLog(String method, dynamic args) { + debugLog( + className: this.runtimeType.toString(), + name: "WebView", + id: getViewId().toString(), + debugLoggingSettings: + PlatformInAppWebViewController.debugLoggingSettings, + method: method, + args: args); + } + + Future _handleMethod(MethodCall call) async { + if (PlatformInAppWebViewController.debugLoggingSettings.enabled && + call.method != "onCallJsHandler") { + _debugLog(call.method, call.arguments); + } + + switch (call.method) { + case "onLoadStart": + _injectedScriptsFromURL.clear(); + if ((webviewParams != null && webviewParams!.onLoadStart != null)) { + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + webviewParams!.onLoadStart!(_controllerFromPlatform, uri); + } + break; + case "onLoadStop": + if ((webviewParams != null && webviewParams!.onLoadStop != null)) { + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + webviewParams!.onLoadStop!(_controllerFromPlatform, uri); + } + break; + case "onConsoleMessage": + if ((webviewParams != null && + webviewParams!.onConsoleMessage != null)) { + Map arguments = + call.arguments.cast(); + ConsoleMessage consoleMessage = ConsoleMessage.fromMap(arguments)!; + webviewParams!.onConsoleMessage!( + _controllerFromPlatform, consoleMessage); + } + break; + case "onScrollChanged": + if ((webviewParams != null && webviewParams!.onScrollChanged != null)) { + int x = call.arguments["x"]; + int y = call.arguments["y"]; + webviewParams!.onScrollChanged!(_controllerFromPlatform, x, y); + } + break; + case "onCreateWindow": + if ((webviewParams != null && webviewParams!.onCreateWindow != null)) { + Map arguments = + call.arguments.cast(); + CreateWindowAction createWindowAction = + CreateWindowAction.fromMap(arguments)!; + + return await webviewParams!.onCreateWindow!( + _controllerFromPlatform, createWindowAction); + } + break; + case "onTitleChanged": + if ((webviewParams != null && webviewParams!.onTitleChanged != null)) { + String? title = call.arguments["title"]; + webviewParams!.onTitleChanged!(_controllerFromPlatform, title); + } + break; + case "onZoomScaleChanged": + if ((webviewParams != null && + // ignore: deprecated_member_use_from_same_package + (webviewParams!.androidOnScaleChanged != null || + webviewParams!.onZoomScaleChanged != null))) { + double oldScale = call.arguments["oldScale"]; + double newScale = call.arguments["newScale"]; + + if (webviewParams!.onZoomScaleChanged != null) + webviewParams!.onZoomScaleChanged!( + _controllerFromPlatform, oldScale, newScale); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnScaleChanged!( + _controllerFromPlatform, oldScale, newScale); + } + } + break; + case "onUpdateVisitedHistory": + if ((webviewParams != null && + webviewParams!.onUpdateVisitedHistory != null)) { + String? url = call.arguments["url"]; + bool? isReload = call.arguments["isReload"]; + WebUri? uri = url != null ? WebUri(url) : null; + webviewParams!.onUpdateVisitedHistory!( + _controllerFromPlatform, uri, isReload); + } + break; + case "onEnterFullscreen": + if (webviewParams != null && webviewParams!.onEnterFullscreen != null) + webviewParams!.onEnterFullscreen!(_controllerFromPlatform); + break; + case "onExitFullscreen": + if (webviewParams != null && webviewParams!.onExitFullscreen != null) + webviewParams!.onExitFullscreen!(_controllerFromPlatform); + break; + case "onWindowFocus": + if (webviewParams != null && webviewParams!.onWindowFocus != null) + webviewParams!.onWindowFocus!(_controllerFromPlatform); + break; + case "onWindowBlur": + if (webviewParams != null && webviewParams!.onWindowBlur != null) + webviewParams!.onWindowBlur!(_controllerFromPlatform); + break; + case "onPrintRequest": + if ((webviewParams != null && + (webviewParams!.onPrintRequest != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.onPrint != null))) { + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + + if (webviewParams!.onPrintRequest != null) + return await webviewParams!.onPrintRequest!( + _controllerFromPlatform, uri, null); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.onPrint!(_controllerFromPlatform, uri); + return false; + } + } + break; + case "onInjectedScriptLoaded": + String id = call.arguments[0]; + var onLoadCallback = _injectedScriptsFromURL[id]?.onLoad; + if ((webviewParams != null) && + onLoadCallback != null) { + onLoadCallback(); + } + break; + case "onInjectedScriptError": + String id = call.arguments[0]; + var onErrorCallback = _injectedScriptsFromURL[id]?.onError; + if ((webviewParams != null) && + onErrorCallback != null) { + onErrorCallback(); + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + return null; + } + + @override + Future getUrl() async { + Map args = {}; + String? url = await channel?.invokeMethod('getUrl', args); + return url != null ? WebUri(url) : null; + } + + @override + Future getTitle() async { + Map args = {}; + return await channel?.invokeMethod('getTitle', args); + } + + @override + Future getHtml() async { + String? html; + + InAppWebViewSettings? settings = await getSettings(); + if (settings != null && settings.javaScriptEnabled == true) { + html = await evaluateJavascript( + source: "window.document.getElementsByTagName('html')[0].outerHTML;"); + if (html != null && html.isNotEmpty) return html; + } + + var webviewUrl = await getUrl(); + if (webviewUrl == null) { + return html; + } + + if (webviewUrl.isScheme("file")) { + var assetPathSplitted = webviewUrl.toString().split("/flutter_assets/"); + var assetPath = assetPathSplitted[assetPathSplitted.length - 1]; + try { + var bytes = await rootBundle.load(assetPath); + html = utf8.decode(bytes.buffer.asUint8List()); + } catch (e) {} + } + + return html; + } + + @override + Future> getFavicons() async { + List favicons = []; + + var webviewUrl = await getUrl(); + + if (webviewUrl == null) { + return favicons; + } + + String? manifestUrl; + + var html = await getHtml(); + if (html == null || html.isEmpty) { + return favicons; + } + var assetPathBase; + + if (webviewUrl.isScheme("file")) { + var assetPathSplitted = webviewUrl.toString().split("/flutter_assets/"); + assetPathBase = assetPathSplitted[0] + "/flutter_assets/"; + } + + InAppWebViewSettings? settings = await getSettings(); + if (settings != null && settings.javaScriptEnabled == true) { + List> links = (await evaluateJavascript(source: """ +(function() { + var linkNodes = document.head.getElementsByTagName("link"); + var links = []; + for (var i = 0; i < linkNodes.length; i++) { + var linkNode = linkNodes[i]; + if (linkNode.rel === 'manifest') { + links.push( + { + rel: linkNode.rel, + href: linkNode.href, + sizes: null + } + ); + } else if (linkNode.rel != null && linkNode.rel.indexOf('icon') >= 0) { + links.push( + { + rel: linkNode.rel, + href: linkNode.href, + sizes: linkNode.sizes != null && linkNode.sizes.value != "" ? linkNode.sizes.value : null + } + ); + } + } + return links; +})(); +"""))?.cast>() ?? []; + for (var link in links) { + if (link["rel"] == "manifest") { + manifestUrl = link["href"]; + if (!_isUrlAbsolute(manifestUrl!)) { + if (manifestUrl.startsWith("/")) { + manifestUrl = manifestUrl.substring(1); + } + manifestUrl = ((assetPathBase == null) + ? webviewUrl.scheme + "://" + webviewUrl.host + "/" + : assetPathBase) + + manifestUrl; + } + continue; + } + favicons.addAll(_createFavicons(webviewUrl, assetPathBase, link["href"], + link["rel"], link["sizes"], false)); + } + } + + return favicons; + } + + bool _isUrlAbsolute(String url) { + return url.startsWith("http://") || url.startsWith("https://"); + } + + List _createFavicons(WebUri url, String? assetPathBase, + String urlIcon, String? rel, String? sizes, bool isManifest) { + List favicons = []; + + List urlSplitted = urlIcon.split("/"); + if (!_isUrlAbsolute(urlIcon)) { + if (urlIcon.startsWith("/")) { + urlIcon = urlIcon.substring(1); + } + urlIcon = ((assetPathBase == null) + ? url.scheme + "://" + url.host + "/" + : assetPathBase) + + urlIcon; + } + if (isManifest) { + rel = (sizes != null) + ? urlSplitted[urlSplitted.length - 1] + .replaceFirst("-" + sizes, "") + .split(" ")[0] + .split(".")[0] + : null; + } + if (sizes != null && sizes.isNotEmpty && sizes != "any") { + List sizesSplitted = sizes.split(" "); + for (String size in sizesSplitted) { + int width = int.parse(size.split("x")[0]); + int height = int.parse(size.split("x")[1]); + favicons.add(Favicon( + url: WebUri(urlIcon), rel: rel, width: width, height: height)); + } + } else { + favicons.add( + Favicon(url: WebUri(urlIcon), rel: rel, width: null, height: null)); + } + + return favicons; + } + + @override + Future loadUrl( + {required URLRequest urlRequest, + @Deprecated('Use allowingReadAccessTo instead') + Uri? iosAllowingReadAccessTo, + WebUri? allowingReadAccessTo}) async { + assert(urlRequest.url != null && urlRequest.url.toString().isNotEmpty); + assert( + allowingReadAccessTo == null || allowingReadAccessTo.isScheme("file")); + assert(iosAllowingReadAccessTo == null || + iosAllowingReadAccessTo.isScheme("file")); + + Map args = {}; + args.putIfAbsent('urlRequest', () => urlRequest.toMap()); + args.putIfAbsent( + 'allowingReadAccessTo', + () => + allowingReadAccessTo?.toString() ?? + iosAllowingReadAccessTo?.toString()); + await channel?.invokeMethod('loadUrl', args); + } + + @override + Future postUrl( + {required WebUri url, required Uint8List postData}) async { + assert(url.toString().isNotEmpty); + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + args.putIfAbsent('postData', () => postData); + await channel?.invokeMethod('postUrl', args); + } + + @override + Future loadData( + {required String data, + String mimeType = "text/html", + String encoding = "utf8", + WebUri? baseUrl, + @Deprecated('Use historyUrl instead') Uri? androidHistoryUrl, + WebUri? historyUrl, + @Deprecated('Use allowingReadAccessTo instead') + Uri? iosAllowingReadAccessTo, + WebUri? allowingReadAccessTo}) async { + assert( + allowingReadAccessTo == null || allowingReadAccessTo.isScheme("file")); + assert(iosAllowingReadAccessTo == null || + iosAllowingReadAccessTo.isScheme("file")); + + Map args = {}; + args.putIfAbsent('data', () => data); + args.putIfAbsent('mimeType', () => mimeType); + args.putIfAbsent('encoding', () => encoding); + args.putIfAbsent('baseUrl', () => baseUrl?.toString() ?? "about:blank"); + args.putIfAbsent( + 'historyUrl', + () => + historyUrl?.toString() ?? + androidHistoryUrl?.toString() ?? + "about:blank"); + args.putIfAbsent( + 'allowingReadAccessTo', + () => + allowingReadAccessTo?.toString() ?? + iosAllowingReadAccessTo?.toString()); + await channel?.invokeMethod('loadData', args); + } + + @override + Future loadFile({required String assetFilePath}) async { + assert(assetFilePath.isNotEmpty); + Map args = {}; + args.putIfAbsent('assetFilePath', () => assetFilePath); + await channel?.invokeMethod('loadFile', args); + } + + @override + Future reload() async { + Map args = {}; + await channel?.invokeMethod('reload', args); + } + + @override + Future goBack() async { + Map args = {}; + await channel?.invokeMethod('goBack', args); + } + + @override + Future goForward() async { + Map args = {}; + await channel?.invokeMethod('goForward', args); + } + + @override + Future goBackOrForward({required int steps}) async { + Map args = {}; + args.putIfAbsent('steps', () => steps); + await channel?.invokeMethod('goBackOrForward', args); + } + + @override + Future isLoading() async { + Map args = {}; + return await channel?.invokeMethod('isLoading', args) ?? false; + } + + @override + Future stopLoading() async { + Map args = {}; + await channel?.invokeMethod('stopLoading', args); + } + + @override + Future evaluateJavascript( + {required String source, ContentWorld? contentWorld}) async { + Map args = {}; + args.putIfAbsent('source', () => source); + args.putIfAbsent('contentWorld', () => contentWorld?.toMap()); + var data = await channel?.invokeMethod('evaluateJavascript', args); + if (data != null) { + try { + // try to json decode the data coming from JavaScript + // otherwise return it as it is. + data = json.decode(data); + } catch (e) {} + } + return data; + } + + @override + Future injectJavascriptFileFromUrl( + {required WebUri urlFile, + ScriptHtmlTagAttributes? scriptHtmlTagAttributes}) async { + assert(urlFile.toString().isNotEmpty); + var id = scriptHtmlTagAttributes?.id; + if (scriptHtmlTagAttributes != null && id != null) { + _injectedScriptsFromURL[id] = scriptHtmlTagAttributes; + } + Map args = {}; + args.putIfAbsent('urlFile', () => urlFile.toString()); + args.putIfAbsent( + 'scriptHtmlTagAttributes', () => scriptHtmlTagAttributes?.toMap()); + await channel?.invokeMethod('injectJavascriptFileFromUrl', args); + } + + @override + Future injectJavascriptFileFromAsset( + {required String assetFilePath}) async { + String source = await rootBundle.loadString(assetFilePath); + return await evaluateJavascript(source: source); + } + + @override + Future injectCSSCode({required String source}) async { + Map args = {}; + args.putIfAbsent('source', () => source); + await channel?.invokeMethod('injectCSSCode', args); + } + + @override + Future injectCSSFileFromUrl( + {required WebUri urlFile, + CSSLinkHtmlTagAttributes? cssLinkHtmlTagAttributes}) async { + assert(urlFile.toString().isNotEmpty); + Map args = {}; + args.putIfAbsent('urlFile', () => urlFile.toString()); + args.putIfAbsent( + 'cssLinkHtmlTagAttributes', () => cssLinkHtmlTagAttributes?.toMap()); + await channel?.invokeMethod('injectCSSFileFromUrl', args); + } + + @override + Future injectCSSFileFromAsset({required String assetFilePath}) async { + String source = await rootBundle.loadString(assetFilePath); + await injectCSSCode(source: source); + } + + @override + @Deprecated('Use setSettings instead') + Future setOptions({required InAppWebViewGroupOptions options}) async { + InAppWebViewSettings settings = + InAppWebViewSettings.fromMap(options.toMap()) ?? InAppWebViewSettings(); + await setSettings(settings: settings); + } + + @override + @Deprecated('Use getSettings instead') + Future getOptions() async { + InAppWebViewSettings? settings = await getSettings(); + + Map? options = settings?.toMap(); + if (options != null) { + options = options.cast(); + return InAppWebViewGroupOptions.fromMap(options as Map); + } + + return null; + } + + @override + Future setSettings({required InAppWebViewSettings settings}) async { + Map args = {}; + + args.putIfAbsent('settings', () => settings.toMap()); + await channel?.invokeMethod('setSettings', args); + } + + @override + Future getSettings() async { + Map args = {}; + + Map? settings = + await channel?.invokeMethod('getSettings', args); + if (settings != null) { + settings = settings.cast(); + return InAppWebViewSettings.fromMap(settings as Map); + } + + return null; + } + + @override + @Deprecated("Use tRexRunnerHtml instead") + Future getTRexRunnerHtml() async { + return await tRexRunnerHtml; + } + + @override + @Deprecated("Use tRexRunnerCss instead") + Future getTRexRunnerCss() async { + return await tRexRunnerCss; + } + + @override + Future scrollTo( + {required int x, required int y, bool animated = false}) async { + Map args = {}; + args.putIfAbsent('x', () => x); + args.putIfAbsent('y', () => y); + args.putIfAbsent('animated', () => animated); + await channel?.invokeMethod('scrollTo', args); + } + + @override + Future scrollBy( + {required int x, required int y, bool animated = false}) async { + Map args = {}; + args.putIfAbsent('x', () => x); + args.putIfAbsent('y', () => y); + args.putIfAbsent('animated', () => animated); + await channel?.invokeMethod('scrollBy', args); + } + + @override + Future printCurrentPage( + {PrintJobSettings? settings}) async { + Map args = {}; + args.putIfAbsent("settings", () => settings?.toMap()); + await channel?.invokeMethod('printCurrentPage', args); + return null; + } + + @override + Future getContentHeight() async { + Map args = {}; + var height = await channel?.invokeMethod('getContentHeight', args); + if (height == null || height == 0) { + // try to use javascript + var scrollHeight = await evaluateJavascript( + source: "document.documentElement.scrollHeight;"); + if (scrollHeight != null && scrollHeight is num) { + height = scrollHeight.toInt(); + } + } + return height; + } + + @override + Future getContentWidth() async { + Map args = {}; + var width = await channel?.invokeMethod('getContentWidth', args); + if (width == null || width == 0) { + // try to use javascript + var scrollHeight = await evaluateJavascript( + source: "document.documentElement.scrollWidth;"); + if (scrollHeight != null && scrollHeight is num) { + width = scrollHeight.toInt(); + } + } + return width; + } + + @override + Future getOriginalUrl() async { + Map args = {}; + String? url = await channel?.invokeMethod('getOriginalUrl', args); + return url != null ? WebUri(url) : null; + } + + @override + Future getSelectedText() async { + Map args = {}; + return await channel?.invokeMethod('getSelectedText', args); + } + + @override + Future> getMetaTags() async { + List metaTags = []; + + List>? metaTagList = + (await evaluateJavascript(source: """ +(function() { + var metaTags = []; + var metaTagNodes = document.head.getElementsByTagName('meta'); + for (var i = 0; i < metaTagNodes.length; i++) { + var metaTagNode = metaTagNodes[i]; + + var otherAttributes = metaTagNode.getAttributeNames(); + var nameIndex = otherAttributes.indexOf("name"); + if (nameIndex !== -1) otherAttributes.splice(nameIndex, 1); + var contentIndex = otherAttributes.indexOf("content"); + if (contentIndex !== -1) otherAttributes.splice(contentIndex, 1); + + var attrs = []; + for (var j = 0; j < otherAttributes.length; j++) { + var otherAttribute = otherAttributes[j]; + attrs.push( + { + name: otherAttribute, + value: metaTagNode.getAttribute(otherAttribute) + } + ); + } + + metaTags.push( + { + name: metaTagNode.name, + content: metaTagNode.content, + attrs: attrs + } + ); + } + return metaTags; +})(); + """))?.cast>(); + + if (metaTagList == null) { + return metaTags; + } + + for (var metaTag in metaTagList) { + var attrs = []; + + for (var metaTagAttr in metaTag["attrs"]) { + attrs.add(MetaTagAttribute( + name: metaTagAttr["name"], value: metaTagAttr["value"])); + } + + metaTags.add(MetaTag( + name: metaTag["name"], content: metaTag["content"], attrs: attrs)); + } + + return metaTags; + } + + @override + Future getMetaThemeColor() async { + Color? themeColor; + + try { + Map args = {}; + themeColor = UtilColor.fromStringRepresentation( + await channel?.invokeMethod('getMetaThemeColor', args)); + return themeColor; + } catch (e) { + // not implemented + } + + // try using javascript + var metaTags = await getMetaTags(); + MetaTag? metaTagThemeColor; + + for (var metaTag in metaTags) { + if (metaTag.name == "theme-color") { + metaTagThemeColor = metaTag; + break; + } + } + + if (metaTagThemeColor == null) { + return null; + } + + var colorValue = metaTagThemeColor.content; + + themeColor = colorValue != null + ? UtilColor.fromStringRepresentation(colorValue) + : null; + + return themeColor; + } + + @override + Future getScrollX() async { + Map args = {}; + return await channel?.invokeMethod('getScrollX', args); + } + + @override + Future getScrollY() async { + Map args = {}; + return await channel?.invokeMethod('getScrollY', args); + } + + @override + Future isSecureContext() async { + Map args = {}; + return await channel?.invokeMethod('isSecureContext', args) ?? false; + } + + @override + Future canScrollVertically() async { + Map args = {}; + return await channel?.invokeMethod('canScrollVertically', args) ?? + false; + } + + @override + Future canScrollHorizontally() async { + Map args = {}; + return await channel?.invokeMethod('canScrollHorizontally', args) ?? + false; + } + + @override + Future get tRexRunnerHtml async => await rootBundle.loadString( + 'packages/flutter_inappwebview/assets/t_rex_runner/t-rex.html'); + + @override + Future get tRexRunnerCss async => await rootBundle.loadString( + 'packages/flutter_inappwebview/assets/t_rex_runner/t-rex.css'); + + @override + Future getIFrameId() async { + Map args = {}; + return await channel?.invokeMethod('getIFrameId', args); + } + + @override + dynamic getViewId() { + return id; + } + + @override + void dispose({bool isKeepAlive = false}) { + disposeChannel(removeMethodCallHandler: true); + webStorage.dispose(); + _controllerFromPlatform = null; + _injectedScriptsFromURL.clear(); + } +} + +extension InternalInAppWebViewController on WebPlatformInAppWebViewController { + get handleMethod => _handleMethod; +} diff --git a/flutter_inappwebview_web/lib/src/in_app_webview/main.dart b/flutter_inappwebview_web/lib/src/in_app_webview/main.dart new file mode 100644 index 00000000..b83b0611 --- /dev/null +++ b/flutter_inappwebview_web/lib/src/in_app_webview/main.dart @@ -0,0 +1,3 @@ +export 'in_app_webview_controller.dart' hide InternalInAppWebViewController; +export 'in_app_webview.dart'; +export 'headless_in_app_webview.dart' hide InternalHeadlessInAppWebView; diff --git a/flutter_inappwebview_web/lib/src/inappwebview_platform.dart b/flutter_inappwebview_web/lib/src/inappwebview_platform.dart new file mode 100644 index 00000000..337103f8 --- /dev/null +++ b/flutter_inappwebview_web/lib/src/inappwebview_platform.dart @@ -0,0 +1,65 @@ +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +import 'cookie_manager.dart'; +import 'in_app_webview/main.dart'; + +/// Implementation of [InAppWebViewPlatform] using the Web API. +class WebPlatformInAppWebViewPlatform extends InAppWebViewPlatform { + /// Registers this class as the default instance of [InAppWebViewPlatform]. + static void registerWith() { + InAppWebViewPlatform.instance = WebPlatformInAppWebViewPlatform(); + } + + /// Creates a new [WebPlatformCookieManager]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [CookieManager] in `flutter_inappwebview` instead. + @override + WebPlatformCookieManager createPlatformCookieManager( + PlatformCookieManagerCreationParams params, + ) { + return WebPlatformCookieManager(params); + } + + /// Creates a new [WebPlatformInAppWebViewController]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [InAppWebViewController] in `flutter_inappwebview` instead. + @override + WebPlatformInAppWebViewController createPlatformInAppWebViewController( + PlatformInAppWebViewControllerCreationParams params, + ) { + return WebPlatformInAppWebViewController(params); + } + + /// Creates a new empty [WebPlatformInAppWebViewController] to access static methods. + /// + /// This function should only be called by the app-facing package. + /// Look at using [InAppWebViewController] in `flutter_inappwebview` instead. + @override + WebPlatformInAppWebViewController createPlatformInAppWebViewControllerStatic() { + return WebPlatformInAppWebViewController.static(); + } + + /// Creates a new [WebPlatformInAppWebViewWidget]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [InAppWebView] in `flutter_inappwebview` instead. + @override + WebPlatformInAppWebViewWidget createPlatformInAppWebViewWidget( + PlatformInAppWebViewWidgetCreationParams params, + ) { + return WebPlatformInAppWebViewWidget(params); + } + + /// Creates a new [WebPlatformHeadlessInAppWebView]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [HeadlessInAppWebView] in `flutter_inappwebview` instead. + @override + WebPlatformHeadlessInAppWebView createPlatformHeadlessInAppWebView( + PlatformHeadlessInAppWebViewCreationParams params, + ) { + return WebPlatformHeadlessInAppWebView(params); + } +} diff --git a/flutter_inappwebview_web/lib/src/main.dart b/flutter_inappwebview_web/lib/src/main.dart new file mode 100644 index 00000000..dd7a9e8f --- /dev/null +++ b/flutter_inappwebview_web/lib/src/main.dart @@ -0,0 +1,4 @@ +export 'inappwebview_platform.dart'; +export 'in_app_webview/main.dart'; +export 'web_storage/main.dart'; +export 'cookie_manager.dart' hide InternalCookieManager; diff --git a/flutter_inappwebview_web/lib/src/platform_util.dart b/flutter_inappwebview_web/lib/src/platform_util.dart new file mode 100644 index 00000000..87fcc8a6 --- /dev/null +++ b/flutter_inappwebview_web/lib/src/platform_util.dart @@ -0,0 +1,50 @@ +import 'package:flutter/services.dart'; + +///Platform native utilities +class PlatformUtil { + static PlatformUtil? _instance; + static const MethodChannel _channel = + MethodChannel('com.pichillilorenzo/flutter_inappwebview_platformutil'); + + PlatformUtil._(); + + ///Get [PlatformUtil] instance. + static PlatformUtil instance() { + return (_instance != null) ? _instance! : _init(); + } + + static PlatformUtil _init() { + _channel.setMethodCallHandler((call) async { + try { + return await _handleMethod(call); + } on Error catch (e) { + print(e); + print(e.stackTrace); + } + }); + _instance = PlatformUtil._(); + return _instance!; + } + + static Future _handleMethod(MethodCall call) async {} + + String? _cachedSystemVersion; + + ///Get current platform system version. + Future getSystemVersion() async { + if (_cachedSystemVersion != null) { + return _cachedSystemVersion!; + } + Map args = {}; + _cachedSystemVersion = + await _channel.invokeMethod('getSystemVersion', args); + return _cachedSystemVersion!; + } + + ///Get cookie expiration date used by Web platform. + Future getWebCookieExpirationDate({required DateTime date}) async { + Map args = {}; + args.putIfAbsent('date', () => date.millisecondsSinceEpoch); + return await _channel.invokeMethod('getWebCookieExpirationDate', args); + } +} diff --git a/flutter_inappwebview_web/lib/src/web_storage/main.dart b/flutter_inappwebview_web/lib/src/web_storage/main.dart new file mode 100644 index 00000000..3132c496 --- /dev/null +++ b/flutter_inappwebview_web/lib/src/web_storage/main.dart @@ -0,0 +1 @@ +export 'web_storage.dart'; diff --git a/flutter_inappwebview_web/lib/src/web_storage/web_storage.dart b/flutter_inappwebview_web/lib/src/web_storage/web_storage.dart new file mode 100644 index 00000000..a1173163 --- /dev/null +++ b/flutter_inappwebview_web/lib/src/web_storage/web_storage.dart @@ -0,0 +1,257 @@ +import 'dart:convert'; + +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +import '../in_app_webview/in_app_webview_controller.dart'; + +/// Object specifying creation parameters for creating a [WebPlatformWebStorage]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformWebStorageCreationParams] for +/// more information. +class WebPlatformWebStorageCreationParams extends PlatformWebStorageCreationParams { + /// Creates a new [WebPlatformWebStorageCreationParams] instance. + WebPlatformWebStorageCreationParams( + {required super.localStorage, required super.sessionStorage}); + + /// Creates a [WebPlatformWebStorageCreationParams] instance based on [PlatformWebStorageCreationParams]. + factory WebPlatformWebStorageCreationParams.fromPlatformWebStorageCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformWebStorageCreationParams params) { + return WebPlatformWebStorageCreationParams( + localStorage: params.localStorage, + sessionStorage: params.sessionStorage); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformWebStorage} +class WebPlatformWebStorage extends PlatformWebStorage { + /// Constructs a [WebPlatformWebStorage]. + WebPlatformWebStorage(PlatformWebStorageCreationParams params) + : super.implementation( + params is WebPlatformWebStorageCreationParams + ? params + : WebPlatformWebStorageCreationParams + .fromPlatformWebStorageCreationParams(params), + ); + + @override + PlatformLocalStorage get localStorage => params.localStorage; + + @override + PlatformSessionStorage get sessionStorage => params.sessionStorage; + + @override + void dispose() { + localStorage.dispose(); + sessionStorage.dispose(); + } +} + +/// Object specifying creation parameters for creating a [WebPlatformStorage]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformStorageCreationParams] for +/// more information. +class WebPlatformStorageCreationParams extends PlatformStorageCreationParams { + /// Creates a new [WebPlatformStorageCreationParams] instance. + WebPlatformStorageCreationParams( + {required super.controller, required super.webStorageType}); + + /// Creates a [WebPlatformStorageCreationParams] instance based on [PlatformStorageCreationParams]. + factory WebPlatformStorageCreationParams.fromPlatformStorageCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformStorageCreationParams params) { + return WebPlatformStorageCreationParams( + controller: params.controller, webStorageType: params.webStorageType); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformStorage} +abstract class WebPlatformStorage implements PlatformStorage { + @override + WebPlatformInAppWebViewController? controller; + + @override + Future length() async { + var result = await controller?.evaluateJavascript(source: """ + window.$webStorageType.length; + """); + return result != null ? int.parse(json.decode(result)) : null; + } + + @override + Future 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)"}); + """); + } + + @override + Future getItem({required String key}) async { + var itemValue = await controller?.evaluateJavascript(source: """ + window.$webStorageType.getItem("$key"); + """); + + if (itemValue == null) { + return null; + } + + try { + return json.decode(itemValue); + } catch (e) {} + + return itemValue; + } + + @override + Future removeItem({required String key}) async { + await controller?.evaluateJavascript(source: """ + window.$webStorageType.removeItem("$key"); + """); + } + + @override + Future> getItems() async { + var webStorageItems = []; + + List>? items = + (await controller?.evaluateJavascript(source: """ +(function() { + var webStorageItems = []; + for(var i = 0; i < window.$webStorageType.length; i++){ + var key = window.$webStorageType.key(i); + webStorageItems.push( + { + key: key, + value: window.$webStorageType.getItem(key) + } + ); + } + return webStorageItems; +})(); + """))?.cast>(); + + if (items == null) { + return webStorageItems; + } + + for (var item in items) { + webStorageItems + .add(WebStorageItem(key: item["key"], value: item["value"])); + } + + return webStorageItems; + } + + @override + Future clear() async { + await controller?.evaluateJavascript(source: """ + window.$webStorageType.clear(); + """); + } + + @override + Future key({required int index}) async { + var result = await controller?.evaluateJavascript(source: """ + window.$webStorageType.key($index); + """); + return result != null ? json.decode(result) : null; + } + + @override + void dispose() { + controller = null; + } +} + +/// Object specifying creation parameters for creating a [WebPlatformLocalStorage]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformLocalStorageCreationParams] for +/// more information. +class WebPlatformLocalStorageCreationParams + extends PlatformLocalStorageCreationParams { + /// Creates a new [WebPlatformLocalStorageCreationParams] instance. + WebPlatformLocalStorageCreationParams(super.params); + + /// Creates a [WebPlatformLocalStorageCreationParams] instance based on [PlatformLocalStorageCreationParams]. + factory WebPlatformLocalStorageCreationParams.fromPlatformLocalStorageCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformLocalStorageCreationParams params) { + return WebPlatformLocalStorageCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformLocalStorage} +class WebPlatformLocalStorage extends PlatformLocalStorage with WebPlatformStorage { + /// Constructs a [WebPlatformLocalStorage]. + WebPlatformLocalStorage(PlatformLocalStorageCreationParams params) + : super.implementation( + params is WebPlatformLocalStorageCreationParams + ? params + : WebPlatformLocalStorageCreationParams + .fromPlatformLocalStorageCreationParams(params), + ); + + /// Default storage + factory WebPlatformLocalStorage.defaultStorage( + {required PlatformInAppWebViewController? controller}) { + return WebPlatformLocalStorage(WebPlatformLocalStorageCreationParams( + PlatformLocalStorageCreationParams(PlatformStorageCreationParams( + controller: controller, + webStorageType: WebStorageType.LOCAL_STORAGE)))); + } + + @override + WebPlatformInAppWebViewController? get controller => + params.controller as WebPlatformInAppWebViewController?; +} + +/// Object specifying creation parameters for creating a [WebPlatformSessionStorage]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformSessionStorageCreationParams] for +/// more information. +class WebPlatformSessionStorageCreationParams + extends PlatformSessionStorageCreationParams { + /// Creates a new [WebPlatformSessionStorageCreationParams] instance. + WebPlatformSessionStorageCreationParams(super.params); + + /// Creates a [WebPlatformSessionStorageCreationParams] instance based on [PlatformSessionStorageCreationParams]. + factory WebPlatformSessionStorageCreationParams.fromPlatformSessionStorageCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformSessionStorageCreationParams params) { + return WebPlatformSessionStorageCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformSessionStorage} +class WebPlatformSessionStorage extends PlatformSessionStorage with WebPlatformStorage { + /// Constructs a [WebPlatformSessionStorage]. + WebPlatformSessionStorage(PlatformSessionStorageCreationParams params) + : super.implementation( + params is WebPlatformSessionStorageCreationParams + ? params + : WebPlatformSessionStorageCreationParams + .fromPlatformSessionStorageCreationParams(params), + ); + + /// Default storage + factory WebPlatformSessionStorage.defaultStorage( + {required PlatformInAppWebViewController? controller}) { + return WebPlatformSessionStorage(WebPlatformSessionStorageCreationParams( + PlatformSessionStorageCreationParams(PlatformStorageCreationParams( + controller: controller, + webStorageType: WebStorageType.SESSION_STORAGE)))); + } + + @override + WebPlatformInAppWebViewController? get controller => + params.controller as WebPlatformInAppWebViewController?; +} diff --git a/lib/src/web/headless_in_app_web_view_web_element.dart b/flutter_inappwebview_web/lib/web/headless_in_app_web_view_web_element.dart similarity index 94% rename from lib/src/web/headless_in_app_web_view_web_element.dart rename to flutter_inappwebview_web/lib/web/headless_in_app_web_view_web_element.dart index fdfd9066..a02c6606 100644 --- a/lib/src/web/headless_in_app_web_view_web_element.dart +++ b/flutter_inappwebview_web/lib/web/headless_in_app_web_view_web_element.dart @@ -4,7 +4,7 @@ import 'package:flutter/services.dart'; import 'headless_inappwebview_manager.dart'; import 'in_app_web_view_web_element.dart'; -import '../../../flutter_inappwebview_platform_interface/lib/src/util.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; class HeadlessInAppWebViewWebElement extends ChannelController { String id; diff --git a/lib/src/web/headless_inappwebview_manager.dart b/flutter_inappwebview_web/lib/web/headless_inappwebview_manager.dart similarity index 96% rename from lib/src/web/headless_inappwebview_manager.dart rename to flutter_inappwebview_web/lib/web/headless_inappwebview_manager.dart index 7d85d908..8a4dc5c9 100644 --- a/lib/src/web/headless_inappwebview_manager.dart +++ b/flutter_inappwebview_web/lib/web/headless_inappwebview_manager.dart @@ -1,13 +1,11 @@ import 'dart:html'; import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; import 'web_platform_manager.dart'; -import '../in_app_webview/in_app_webview_settings.dart'; import 'in_app_web_view_web_element.dart'; import 'headless_in_app_web_view_web_element.dart'; -import '../types/main.dart'; - class HeadlessInAppWebViewManager { static final Map webViews = {}; diff --git a/lib/src/web/in_app_web_view_web_element.dart b/flutter_inappwebview_web/lib/web/in_app_web_view_web_element.dart similarity index 98% rename from lib/src/web/in_app_web_view_web_element.dart rename to flutter_inappwebview_web/lib/web/in_app_web_view_web_element.dart index cc38e2ae..f5fe56a6 100644 --- a/lib/src/web/in_app_web_view_web_element.dart +++ b/flutter_inappwebview_web/lib/web/in_app_web_view_web_element.dart @@ -2,15 +2,12 @@ import 'dart:async'; import 'dart:typed_data'; import 'dart:ui'; import 'package:flutter/services.dart'; -import '../../../flutter_inappwebview_platform_interface/lib/src/web_uri.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; import 'dart:html'; import 'dart:js' as js; import 'headless_inappwebview_manager.dart'; import 'web_platform_manager.dart'; -import '../in_app_webview/in_app_webview_settings.dart'; -import '../types/main.dart'; -import '../types/disposable.dart'; class InAppWebViewWebElement implements Disposable { late dynamic _viewId; @@ -157,6 +154,8 @@ class InAppWebViewWebElement implements Disposable { break; case "getContentHeight": return await getContentHeight(); + case "getContentWidth": + return await getContentWidth(); case "getOriginalUrl": return await getOriginalUrl(); case "getSelectedText": @@ -392,6 +391,10 @@ class InAppWebViewWebElement implements Disposable { return (_callMethod('getContentHeight') as num?)?.toInt(); } + Future getContentWidth() async { + return (_callMethod('getContentWidth') as num?)?.toInt(); + } + Future getOriginalUrl() async { return iframe.src; } diff --git a/lib/src/web/main.dart b/flutter_inappwebview_web/lib/web/main.dart similarity index 100% rename from lib/src/web/main.dart rename to flutter_inappwebview_web/lib/web/main.dart diff --git a/lib/src/web/platform_util.dart b/flutter_inappwebview_web/lib/web/platform_util.dart similarity index 92% rename from lib/src/web/platform_util.dart rename to flutter_inappwebview_web/lib/web/platform_util.dart index 271bf8e8..294503c8 100644 --- a/lib/src/web/platform_util.dart +++ b/flutter_inappwebview_web/lib/web/platform_util.dart @@ -1,6 +1,6 @@ import 'dart:async'; import 'package:flutter/services.dart'; -import '../../../flutter_inappwebview_platform_interface/lib/src/util.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; import 'dart:js' as js; diff --git a/lib/src/web/web_platform.dart b/flutter_inappwebview_web/lib/web/web_platform.dart similarity index 91% rename from lib/src/web/web_platform.dart rename to flutter_inappwebview_web/lib/web/web_platform.dart index 04bf98d1..3cca5ab0 100644 --- a/lib/src/web/web_platform.dart +++ b/flutter_inappwebview_web/lib/web/web_platform.dart @@ -1,8 +1,9 @@ import 'dart:async'; +import 'dart:ui_web'; +import '../src/inappwebview_platform.dart'; import 'headless_inappwebview_manager.dart'; import 'web_platform_manager.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; -import 'shims/dart_ui.dart' as ui; import 'in_app_web_view_web_element.dart'; import 'platform_util.dart'; @@ -11,10 +12,10 @@ import 'package:js/js.dart'; /// Builds an iframe based WebView. /// /// This is used as the default implementation for `WebView` on web. -class FlutterInAppWebViewWebPlatform { - /// Constructs a new instance of [FlutterInAppWebViewWebPlatform]. - FlutterInAppWebViewWebPlatform(Registrar registrar) { - ui.platformViewRegistry.registerViewFactory( +class InAppWebViewFlutterPlugin { + /// Constructs a new instance of [InAppWebViewFlutterPlugin]. + InAppWebViewFlutterPlugin(Registrar registrar) { + platformViewRegistry.registerViewFactory( 'com.pichillilorenzo/flutter_inappwebview', (int viewId) { var webView = InAppWebViewWebElement(viewId: viewId, messenger: registrar); @@ -24,8 +25,9 @@ class FlutterInAppWebViewWebPlatform { } static void registerWith(Registrar registrar) { + WebPlatformInAppWebViewPlatform.registerWith(); // ignore: unused_local_variable - final pluginInstance = FlutterInAppWebViewWebPlatform(registrar); + final pluginInstance = InAppWebViewFlutterPlugin(registrar); // ignore: unused_local_variable final platformUtil = PlatformUtil(messenger: registrar); // ignore: unused_local_variable diff --git a/lib/src/web/web_platform_manager.dart b/flutter_inappwebview_web/lib/web/web_platform_manager.dart similarity index 100% rename from lib/src/web/web_platform_manager.dart rename to flutter_inappwebview_web/lib/web/web_platform_manager.dart diff --git a/flutter_inappwebview_web/pubspec.yaml b/flutter_inappwebview_web/pubspec.yaml new file mode 100644 index 00000000..0f50e021 --- /dev/null +++ b/flutter_inappwebview_web/pubspec.yaml @@ -0,0 +1,85 @@ +name: flutter_inappwebview_web +description: Web implementation of the flutter_inappwebview plugin. +version: 1.0.0 +homepage: https://inappwebview.dev/ +repository: https://github.com/pichillilorenzo/flutter_inappwebview/tree/master/flutter_inappwebview_web +issue_tracker: https://github.com/pichillilorenzo/flutter_inappwebview/issues +topics: + - html + - webview + - webview-flutter + - inappwebview + - browser + +environment: + sdk: ">=2.17.0 <4.0.0" + flutter: ">=3.0.0" + +dependencies: + flutter: + sdk: flutter + flutter_web_plugins: + sdk: flutter + js: ^0.6.4 + flutter_inappwebview_platform_interface: + path: ../flutter_inappwebview_platform_interface + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^2.0.0 + plugin_platform_interface: ^2.0.2 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + # This section identifies this Flutter project as a plugin project. + # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) + # which should be registered in the plugin registry. This is required for + # using method channels. + # The Android 'package' specifies package in which the registered class is. + # This is required for using method channels on Android. + # The 'ffiPlugin' specifies that native code should be built and bundled. + # This is required for using `dart:ffi`. + # All these are used by the tooling to maintain consistency when + # adding or updating assets for this project. + plugin: + platforms: + web: + pluginClass: InAppWebViewFlutterPlugin + fileName: web/main.dart + + assets: + - packages/flutter_inappwebview_web/assets/web/web_support.js + # To add assets to your plugin package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your plugin package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/flutter_inappwebview_web/test/flutter_inappwebview_web_test.dart b/flutter_inappwebview_web/test/flutter_inappwebview_web_test.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/flutter_inappwebview.dart b/lib/flutter_inappwebview.dart index 419ed0fb..b796ace9 100755 --- a/lib/flutter_inappwebview.dart +++ b/lib/flutter_inappwebview.dart @@ -34,4 +34,3 @@ export 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_pla MapSize, MapEdgeInsets; export 'src/main.dart'; -export 'src/web/main_stub.dart' if (dart.library.html) 'src/web/main.dart'; diff --git a/lib/src/web/main_stub.dart b/lib/src/web/main_stub.dart deleted file mode 100644 index c33f7f2c..00000000 --- a/lib/src/web/main_stub.dart +++ /dev/null @@ -1,4 +0,0 @@ -///Stub for web support. -class FlutterInAppWebViewWebPlatform { - static void registerWith(dynamic registrar) {} -} diff --git a/lib/src/web/shims/dart_ui.dart b/lib/src/web/shims/dart_ui.dart deleted file mode 100644 index 4361b399..00000000 --- a/lib/src/web/shims/dart_ui.dart +++ /dev/null @@ -1 +0,0 @@ -export 'dart_ui_fake.dart' if (dart.library.html) 'dart_ui_real.dart'; diff --git a/lib/src/web/shims/dart_ui_fake.dart b/lib/src/web/shims/dart_ui_fake.dart deleted file mode 100644 index 2e5834ce..00000000 --- a/lib/src/web/shims/dart_ui_fake.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'dart:html' as html; - -// Fake interface for the logic that this package needs from (web-only) dart:ui. -// This is conditionally exported so the analyzer sees these methods as available. - -// ignore_for_file: avoid_classes_with_only_static_members -// ignore_for_file: camel_case_types - -/// Shim for web_ui engine.PlatformViewRegistry -/// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L62 -class platformViewRegistry { - /// Shim for registerViewFactory - /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L72 - static bool registerViewFactory( - String viewTypeId, html.Element Function(int viewId) viewFactory) { - return false; - } -} - -/// Shim for web_ui engine.AssetManager. -/// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/src/engine/assets.dart#L12 -class webOnlyAssetManager { - /// Shim for getAssetUrl. - /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/src/engine/assets.dart#L45 - static String getAssetUrl(String asset) => ''; -} - -/// Signature of callbacks that have no arguments and return no data. -typedef VoidCallback = void Function(); diff --git a/lib/src/web/shims/dart_ui_real.dart b/lib/src/web/shims/dart_ui_real.dart deleted file mode 100644 index 69c06ee2..00000000 --- a/lib/src/web/shims/dart_ui_real.dart +++ /dev/null @@ -1 +0,0 @@ -export 'dart:ui'; diff --git a/pubspec.yaml b/pubspec.yaml index e97a456e..3305c6f4 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -18,9 +18,6 @@ environment: dependencies: flutter: sdk: flutter - flutter_web_plugins: - sdk: flutter - js: ^0.6.4 flutter_inappwebview_platform_interface: path: flutter_inappwebview_platform_interface flutter_inappwebview_android: @@ -29,6 +26,8 @@ dependencies: path: flutter_inappwebview_ios flutter_inappwebview_macos: path: flutter_inappwebview_macos + flutter_inappwebview_web: + path: flutter_inappwebview_web dev_dependencies: flutter_test: @@ -54,13 +53,11 @@ flutter: macos: default_package: flutter_inappwebview_macos web: - pluginClass: FlutterInAppWebViewWebPlatform - fileName: flutter_inappwebview.dart + default_package: flutter_inappwebview_web assets: - packages/flutter_inappwebview/assets/t_rex_runner/t-rex.html - packages/flutter_inappwebview/assets/t_rex_runner/t-rex.css - - packages/flutter_inappwebview/assets/web/web_support.js # To add assets to your plugin package, add an assets section, like this: # assets: # - images/a_dot_burr.jpeg