diff --git a/CHANGELOG.md b/CHANGELOG.md index b587ae3..7f9a350 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## 0.2.1 + +- Breaking change + + - WebViewX width and height are now required (due to the fact that the web version always needs a width and a height) + +- Added the option to use hybrid composition for Android WebViews in `MobileSpecificParams` +- Added a new public default CORS proxy service for Web +- Update dependencies + ## 0.2.0 - Deprecated pedantic. Adopted lint instead. diff --git a/README.md b/README.md index 4d32da5..d5161a4 100644 --- a/README.md +++ b/README.md @@ -114,8 +114,8 @@ Note: For more detailed information about things such as `EmbeddedJsContent`, pl | `String` initialContent | Initial webview content | | `SourceType` initialSourceType | Initial webview content type (`url, urlBypass or html`) | | `String?` userAgent | User agent | -| `double?` width | Widget's width (if null, it takes all available space) | -| `double?` height | Widget's height (if null, it takes all available space) | +| `double` width | Widget's width | +| `double` height | Widget's height | | `Function(WebViewXController controller)?` onWebViewCreated | Callback that gets executed when the webview has initialized | | `Set` jsContent | A set of EmbeddedJsContent, which is an object that defines some javascript which will be embedded in the page, once loaded (check the example app) | | `Set` dartCallBacks | A set of DartCallback, which is an object that defines a dart callback function, which will be called from javascript (check the example app) | diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml new file mode 100644 index 0000000..9e232d8 --- /dev/null +++ b/example/analysis_options.yaml @@ -0,0 +1,5 @@ +include: package:lint/analysis_options_package.yaml +linter: + rules: + # Disable + use_build_context_synchronously: false diff --git a/example/lib/webview_page.dart b/example/lib/webview_page.dart index c81fe13..01e41b3 100644 --- a/example/lib/webview_page.dart +++ b/example/lib/webview_page.dart @@ -80,6 +80,8 @@ class _WebViewXPageState extends State { key: const ValueKey('webviewx'), initialContent: initialContent, initialSourceType: SourceType.html, + height: screenSize.height / 2, + width: min(screenSize.width * 0.8, 1024), onWebViewCreated: (controller) => webviewController = controller, onPageStarted: (src) => debugPrint('A new page has started loading: $src\n'), @@ -105,12 +107,13 @@ class _WebViewXPageState extends State { webSpecificParams: const WebSpecificParams( printDebugInfo: true, ), + mobileSpecificParams: const MobileSpecificParams( + androidEnableHybridComposition: true, + ), navigationDelegate: (navigation) { debugPrint(navigation.content.sourceType.toString()); return NavigationDecision.navigate; }, - height: screenSize.height / 2, - width: min(screenSize.width * 0.8, 1024), ); } diff --git a/example/pubspec.lock b/example/pubspec.lock index 955c954..9f356e2 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -7,7 +7,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0" + version: "2.6.1" boolean_selector: dependency: transitive description: @@ -89,12 +89,12 @@ packages: source: hosted version: "4.0.0" lint: - dependency: transitive + dependency: "direct dev" description: name: lint url: "https://pub.dartlang.org" source: hosted - version: "1.5.3" + version: "1.6.0" matcher: dependency: transitive description: @@ -141,7 +141,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.1" stack_trace: dependency: transitive description: @@ -176,7 +176,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19" + version: "0.3.0" typed_data: dependency: transitive description: @@ -204,14 +204,14 @@ packages: name: webview_flutter url: "https://pub.dartlang.org" source: hosted - version: "2.0.12" + version: "2.0.13" webviewx: dependency: "direct main" description: path: ".." relative: true source: path - version: "0.2.0" + version: "0.2.1" sdks: - dart: ">=2.12.0 <3.0.0" + dart: ">=2.13.0 <3.0.0" flutter: ">=2.0.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 19227fe..4ecee15 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -19,6 +19,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + lint: ^1.5.3 flutter: uses-material-design: true diff --git a/lib/src/controller/impl/mobile.dart b/lib/src/controller/impl/mobile.dart index 70bb9da..d3c2be9 100644 --- a/lib/src/controller/impl/mobile.dart +++ b/lib/src/controller/impl/mobile.dart @@ -28,7 +28,7 @@ class WebViewXController extends ChangeNotifier required String initialContent, required SourceType initialSourceType, required bool ignoreAllGestures, - }) : _ignoreAllGesturesNotifier = ValueNotifier(ignoreAllGestures), + }) : _ignoreAllGesturesNotifier = ValueNotifier(ignoreAllGestures), value = WebViewContent( source: initialContent, sourceType: initialSourceType, diff --git a/lib/src/controller/impl/web.dart b/lib/src/controller/impl/web.dart index 43c9762..ec69923 100644 --- a/lib/src/controller/impl/web.dart +++ b/lib/src/controller/impl/web.dart @@ -33,7 +33,7 @@ class WebViewXController extends ChangeNotifier required String initialContent, required SourceType initialSourceType, required bool ignoreAllGestures, - }) : _ignoreAllGesturesNotifier = ValueNotifier(ignoreAllGestures), + }) : _ignoreAllGesturesNotifier = ValueNotifier(ignoreAllGestures), _history = HistoryStack( initialEntry: WebViewContent( source: initialContent, diff --git a/lib/src/utils/mobile_specific_params.dart b/lib/src/utils/mobile_specific_params.dart index 8a88a4b..45e5e98 100644 --- a/lib/src/utils/mobile_specific_params.dart +++ b/lib/src/utils/mobile_specific_params.dart @@ -40,10 +40,14 @@ class MobileSpecificParams { /// Same as the original one from webview_flutter. final bool gestureNavigationEnabled; + /// Enable WebView hybrid composition + final bool androidEnableHybridComposition; + /// Constructor const MobileSpecificParams({ this.mobileGestureRecognizers, this.gestureNavigationEnabled = false, this.debuggingEnabled = false, + this.androidEnableHybridComposition = false, }); } diff --git a/lib/src/utils/web_url_bypass_proxy.dart b/lib/src/utils/web_url_bypass_proxy.dart index 54a60f1..e496947 100644 --- a/lib/src/utils/web_url_bypass_proxy.dart +++ b/lib/src/utils/web_url_bypass_proxy.dart @@ -10,6 +10,7 @@ abstract class BypassProxy { static const publicProxies = [ BridgedBypassProxy(), CodeTabsBypassProxy(), + WeCorsAnyWhereProxy(), ]; } @@ -43,6 +44,21 @@ class CodeTabsBypassProxy implements BypassProxy { } } +/// we-cors-anywhere.herokuapp.com proxy +class WeCorsAnyWhereProxy implements BypassProxy { + const WeCorsAnyWhereProxy(); + + @override + String buildProxyUrl(String pageUrl) { + return 'https://we-cors-anywhere.herokuapp.com/$pageUrl'; + } + + @override + String extractPageSource(String responseBody) { + return responseBody; + } +} + /* Example for when the proxy's response is not the page source directly, but instead it's a JSON object. diff --git a/lib/src/view/impl/facade.dart b/lib/src/view/impl/facade.dart index 34a113c..a32dfc0 100644 --- a/lib/src/view/impl/facade.dart +++ b/lib/src/view/impl/facade.dart @@ -24,11 +24,11 @@ class WebViewX extends StatelessWidget implements view_interface.WebViewX { /// Widget width @override - final double? width; + final double width; /// Widget height @override - final double? height; + final double height; /// Callback which returns a referrence to the [WebViewXController] /// being created. @@ -103,8 +103,8 @@ class WebViewX extends StatelessWidget implements view_interface.WebViewX { this.initialContent = 'about:blank', this.initialSourceType = SourceType.url, this.userAgent, - this.width, - this.height, + required this.width, + required this.height, this.onWebViewCreated, this.jsContent = const {}, this.dartCallBacks = const {}, diff --git a/lib/src/view/impl/io.dart b/lib/src/view/impl/io.dart index 93f7622..27e4d5d 100644 --- a/lib/src/view/impl/io.dart +++ b/lib/src/view/impl/io.dart @@ -30,11 +30,11 @@ class WebViewX extends StatelessWidget implements view_interface.WebViewX { /// Widget width @override - final double? width; + final double width; /// Widget height @override - final double? height; + final double height; /// Callback which returns a referrence to the [WebViewXController] /// being created. @@ -109,8 +109,8 @@ class WebViewX extends StatelessWidget implements view_interface.WebViewX { this.initialContent = 'about:blank', this.initialSourceType = SourceType.url, this.userAgent, - this.width, - this.height, + required this.width, + required this.height, this.onWebViewCreated, this.jsContent = const {}, this.dartCallBacks = const {}, @@ -128,27 +128,25 @@ class WebViewX extends StatelessWidget implements view_interface.WebViewX { @override Widget build(BuildContext context) { - return LayoutBuilder(builder: (context, constraints) { - return mobile.WebViewX( - key: key, - initialContent: initialContent, - initialSourceType: initialSourceType, - userAgent: userAgent, - width: width ?? constraints.maxWidth, - height: height ?? constraints.maxHeight, - dartCallBacks: dartCallBacks, - jsContent: jsContent, - onWebViewCreated: onWebViewCreated, - ignoreAllGestures: ignoreAllGestures, - javascriptMode: javascriptMode, - initialMediaPlaybackPolicy: initialMediaPlaybackPolicy, - onPageStarted: onPageStarted, - onPageFinished: onPageFinished, - navigationDelegate: navigationDelegate, - onWebResourceError: onWebResourceError, - webSpecificParams: webSpecificParams, - mobileSpecificParams: mobileSpecificParams, - ); - }); + return mobile.WebViewX( + key: key, + initialContent: initialContent, + initialSourceType: initialSourceType, + userAgent: userAgent, + width: width, + height: height, + dartCallBacks: dartCallBacks, + jsContent: jsContent, + onWebViewCreated: onWebViewCreated, + ignoreAllGestures: ignoreAllGestures, + javascriptMode: javascriptMode, + initialMediaPlaybackPolicy: initialMediaPlaybackPolicy, + onPageStarted: onPageStarted, + onPageFinished: onPageFinished, + navigationDelegate: navigationDelegate, + onWebResourceError: onWebResourceError, + webSpecificParams: webSpecificParams, + mobileSpecificParams: mobileSpecificParams, + ); } } diff --git a/lib/src/view/impl/mobile.dart b/lib/src/view/impl/mobile.dart index f6978d6..dc92ccd 100644 --- a/lib/src/view/impl/mobile.dart +++ b/lib/src/view/impl/mobile.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -32,11 +33,11 @@ class WebViewX extends StatefulWidget implements view_interface.WebViewX { /// Widget width @override - final double? width; + final double width; /// Widget height @override - final double? height; + final double height; /// Callback which returns a referrence to the [WebViewXController] /// being created. @@ -111,8 +112,8 @@ class WebViewX extends StatefulWidget implements view_interface.WebViewX { this.initialContent = 'about:blank', this.initialSourceType = SourceType.url, this.userAgent, - this.width, - this.height, + required this.width, + required this.height, this.onWebViewCreated, this.jsContent = const {}, this.dartCallBacks = const {}, @@ -142,6 +143,11 @@ class _WebViewXState extends State { void initState() { super.initState(); + if (Platform.isAndroid && + widget.mobileSpecificParams.androidEnableHybridComposition) { + wf.WebView.platform = wf.SurfaceAndroidWebView(); + } + _ignoreAllGestures = widget.ignoreAllGestures; webViewXController = _createWebViewXController(); } @@ -220,33 +226,31 @@ class _WebViewXState extends State { ) .toSet(); - final webview = SizedBox( + return SizedBox( width: widget.width, height: widget.height, - child: wf.WebView( - key: widget.key, - initialUrl: _initialContent(), - javascriptMode: javascriptMode, - onWebViewCreated: onWebViewCreated, - javascriptChannels: javascriptChannels, - gestureRecognizers: - widget.mobileSpecificParams.mobileGestureRecognizers, - onPageStarted: widget.onPageStarted, - onPageFinished: widget.onPageFinished, - initialMediaPlaybackPolicy: initialMediaPlaybackPolicy, - onWebResourceError: onWebResourceError, - gestureNavigationEnabled: - widget.mobileSpecificParams.gestureNavigationEnabled, - debuggingEnabled: widget.mobileSpecificParams.debuggingEnabled, - navigationDelegate: navigationDelegate, - userAgent: widget.userAgent, + child: IgnorePointer( + ignoring: _ignoreAllGestures, + child: wf.WebView( + key: widget.key, + initialUrl: _initialContent(), + javascriptMode: javascriptMode, + onWebViewCreated: onWebViewCreated, + javascriptChannels: javascriptChannels, + gestureRecognizers: + widget.mobileSpecificParams.mobileGestureRecognizers, + onPageStarted: widget.onPageStarted, + onPageFinished: widget.onPageFinished, + initialMediaPlaybackPolicy: initialMediaPlaybackPolicy, + onWebResourceError: onWebResourceError, + gestureNavigationEnabled: + widget.mobileSpecificParams.gestureNavigationEnabled, + debuggingEnabled: widget.mobileSpecificParams.debuggingEnabled, + navigationDelegate: navigationDelegate, + userAgent: widget.userAgent, + ), ), ); - - return IgnorePointer( - ignoring: _ignoreAllGestures, - child: webview, - ); } // Returns initial data diff --git a/lib/src/view/impl/web.dart b/lib/src/view/impl/web.dart index 83cb291..6f747e5 100644 --- a/lib/src/view/impl/web.dart +++ b/lib/src/view/impl/web.dart @@ -37,11 +37,11 @@ class WebViewX extends StatefulWidget implements view_interface.WebViewX { /// Widget width @override - final double? width; + final double width; /// Widget height @override - final double? height; + final double height; /// Callback which returns a referrence to the [WebViewXController] /// being created. @@ -116,8 +116,8 @@ class WebViewX extends StatefulWidget implements view_interface.WebViewX { this.initialContent = 'about:blank', this.initialSourceType = SourceType.url, this.userAgent, - this.width, - this.height, + required this.width, + required this.height, this.onWebViewCreated, this.jsContent = const {}, this.dartCallBacks = const {}, @@ -303,18 +303,25 @@ class _WebViewXState extends State { final htmlElementView = SizedBox( width: widget.width, height: widget.height, - child: _htmlElement(iframeViewType), + child: AbsorbPointer( + child: RepaintBoundary( + child: HtmlElementView( + key: widget.key, + viewType: iframeViewType, + ), + ), + ), ); return _iframeIgnorePointer( - child: htmlElementView, ignoring: _ignoreAllGestures, + child: htmlElementView, ); } Widget _iframeIgnorePointer({ - required Widget child, bool ignoring = false, + required Widget child, }) { return Stack( children: [ @@ -331,17 +338,6 @@ class _WebViewXState extends State { ); } - Widget _htmlElement(String iframeViewType) { - return AbsorbPointer( - child: RepaintBoundary( - child: HtmlElementView( - key: widget.key, - viewType: iframeViewType, - ), - ), - ); - } - // This creates a unique String to be used as the view type of the HtmlElementView String _createViewType() { return HtmlUtils.buildIframeViewType(); @@ -352,8 +348,8 @@ class _WebViewXState extends State { ..id = 'id_$iframeViewType' ..name = 'name_$iframeViewType' ..style.border = 'none' - ..width = widget.width!.toInt().toString() - ..height = widget.height!.toInt().toString() + ..width = widget.width.toInt().toString() + ..height = widget.height.toInt().toString() ..allowFullscreen = widget.webSpecificParams.webAllowFullscreenContent; widget.webSpecificParams.additionalSandboxOptions.forEach( @@ -553,7 +549,7 @@ class _WebViewXState extends State { final proxy = proxyList[i]; _debugLog('Using proxy: ${proxy.runtimeType}'); - final proxiedUri = Uri.parse(proxy.buildProxyUrl(url)); + final proxiedUri = Uri.parse(proxy.buildProxyUrl(Uri.encodeFull(url))); Future request; diff --git a/lib/src/view/interface.dart b/lib/src/view/interface.dart index b3e5a06..4a7661a 100644 --- a/lib/src/view/interface.dart +++ b/lib/src/view/interface.dart @@ -18,10 +18,10 @@ abstract class WebViewX { final String? userAgent; /// Widget width - final double? width; + final double width; /// Widget height - final double? height; + final double height; /// Callback which returns a referrence to the [IWebViewXController] /// being created. @@ -82,8 +82,8 @@ abstract class WebViewX { this.initialContent = 'about:blank', this.initialSourceType = SourceType.url, this.userAgent, - this.width, - this.height, + required this.width, + required this.height, this.onWebViewCreated, this.jsContent = const {}, this.dartCallBacks = const {}, diff --git a/pubspec.lock b/pubspec.lock index fe088f4..e1c3fd8 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,7 +7,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0" + version: "2.6.1" boolean_selector: dependency: transitive description: @@ -82,12 +82,12 @@ packages: source: hosted version: "4.0.0" lint: - dependency: "direct main" + dependency: "direct dev" description: name: lint url: "https://pub.dartlang.org" source: hosted - version: "1.5.3" + version: "1.6.0" matcher: dependency: transitive description: @@ -134,7 +134,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.1" stack_trace: dependency: transitive description: @@ -169,7 +169,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19" + version: "0.3.0" typed_data: dependency: transitive description: @@ -197,7 +197,7 @@ packages: name: webview_flutter url: "https://pub.dartlang.org" source: hosted - version: "2.0.12" + version: "2.0.13" sdks: - dart: ">=2.12.0 <3.0.0" + dart: ">=2.13.0 <3.0.0" flutter: ">=2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 78e176f..7a05af0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: webviewx description: A feature-rich cross-platform webview using webview_flutter for mobile and iframe for web. JS interop-ready. homepage: https://github.com/adrianflutur/webviewx -version: 0.2.0 +version: 0.2.1 environment: sdk: ">=2.12.0 <3.0.0" @@ -12,12 +12,12 @@ dependencies: flutter: sdk: flutter http: ^0.13.3 - lint: ^1.5.3 path: ^1.8.0 pointer_interceptor: ^0.9.0+1 uuid: ^3.0.4 - webview_flutter: ^2.0.12 + webview_flutter: ^2.0.13 dev_dependencies: flutter_test: sdk: flutter + lint: ^1.6.0