From 541cdb64f5874b7d5b7a8ec54eb3aae8a6f7f6da Mon Sep 17 00:00:00 2001 From: dab246 Date: Thu, 17 Feb 2022 15:04:52 +0700 Subject: [PATCH] TF-261 Migrate to webview_flutter --- .../html_content_viewer_widget.dart | 196 ++++++++++-------- core/pubspec.yaml | 10 +- 2 files changed, 115 insertions(+), 91 deletions(-) diff --git a/core/lib/presentation/views/html_viewer/html_content_viewer_widget.dart b/core/lib/presentation/views/html_viewer/html_content_viewer_widget.dart index e39741626..3d0c09c8d 100644 --- a/core/lib/presentation/views/html_viewer/html_content_viewer_widget.dart +++ b/core/lib/presentation/views/html_viewer/html_content_viewer_widget.dart @@ -1,10 +1,13 @@ +import 'dart:async'; + import 'package:core/core.dart'; import 'package:easy_web_view/easy_web_view.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:url_launcher/url_launcher.dart' as launcher; +import 'package:webview_flutter/webview_flutter.dart'; +import 'dart:developer' as developer; class HtmlContentViewer extends StatefulWidget { @@ -14,16 +17,17 @@ class HtmlContentViewer extends StatefulWidget { final double? heightContent; final Widget? loadingWidget; - /// Register this callback if you want a reference to the [InAppWebViewController]. - final void Function(InAppWebViewController controller)? onCreated; - - final void Function(InAppWebViewController controller, Uri? uri)? onLoadStart; - - final void Function(InAppWebViewController controller, Uri? uri)? onLoadStop; + /// Register this callback if you want a reference to the [WebViewController]. + final void Function(WebViewController controller)? onCreated; /// Handler for mailto: links final Future Function(Uri mailto)? mailtoDelegate; + /// Handler for any non-media URLs that the user taps on the website. + /// + /// Returns `true` when the given `url` was handled. + final Future Function(String url)? urlLauncherDelegate; + const HtmlContentViewer({ Key? key, required this.contentHtml, @@ -32,8 +36,7 @@ class HtmlContentViewer extends StatefulWidget { this.minHeight = 100, this.loadingWidget, this.onCreated, - this.onLoadStart, - this.onLoadStop, + this.urlLauncherDelegate, this.mailtoDelegate, }) : super(key: key); @@ -43,16 +46,17 @@ class HtmlContentViewer extends StatefulWidget { class _HtmlContentViewState extends State { - double? _documentHeight = 1.0; - double? _documentWidth = 1.0; - String? _initialPageContent; - late InAppWebViewController _webViewController; - bool loading = true; + double? _webViewHeight = 1.0; + double? _webViewWidth = 1.0; + String? _htmlData; + late WebViewController _webViewController; + // late EasyWebViewControllerWrapperBase _easyWebViewControllerWrapperBase; + bool _isLoading = true; @override void initState() { super.initState(); - _initialPageContent = _generateHtmlDocument(widget.contentHtml); + _htmlData = _generateHtmlDocument(widget.contentHtml); } String _generateHtmlDocument(String content) { @@ -103,18 +107,24 @@ class _HtmlContentViewState extends State { @override Widget build(BuildContext context) { if (kIsWeb) { - return _buildWebViewOnBrowser(); + return Stack( + alignment: AlignmentDirectional.center, + children: [ + _buildWebViewOnBrowser(), + if (_isLoading) _buildLoadingView() + ], + ); } else { - _documentWidth = widget.widthContent; + _webViewWidth = widget.widthContent; return Stack( alignment: AlignmentDirectional.center, children: [ SizedBox( - height: _documentHeight, - width: _documentWidth, + height: _webViewHeight, + width: _webViewWidth, child: _buildWebView(), ), - if (loading) _buildLoadingView() + if (_isLoading) _buildLoadingView() ], ); } @@ -134,99 +144,113 @@ class _HtmlContentViewState extends State { } Widget _buildWebViewOnBrowser() { - if (_initialPageContent == null || _initialPageContent?.isEmpty == true) { + final htmlData = _htmlData; + if (htmlData == null || htmlData.isEmpty) { return Container(); } return EasyWebView( - key: ValueKey('WebViewOnBrowser'), - src: _initialPageContent!, - onLoaded: () => {}, + key: ValueKey(htmlData), + src: htmlData, + onLoaded: (controller) async { + // _easyWebViewControllerWrapperBase = controller; + if (mounted && _isLoading) { + setState(() { + _isLoading = false; + }); + } + }, isHtml: true, - webNavigationDelegate: (_) => WebNavigationDecision.prevent, height: widget.heightContent, + webNavigationDelegate: _onNavigationOnWebBrowser, ); } Widget _buildWebView() { - if (_initialPageContent == null || _initialPageContent?.isEmpty == true) { + final htmlData = _htmlData; + if (htmlData == null || htmlData.isEmpty) { return Container(); } - return InAppWebView( - key: ValueKey(_initialPageContent), - initialData: InAppWebViewInitialData(data: _initialPageContent!), - initialOptions: InAppWebViewGroupOptions( - crossPlatform: InAppWebViewOptions( - useShouldOverrideUrlLoading: true, - verticalScrollBarEnabled: false, - disableVerticalScroll: false, - disableHorizontalScroll: false, - supportZoom: true, - ), - android: AndroidInAppWebViewOptions( - useWideViewPort: false, - loadWithOverviewMode: true, - useHybridComposition: true, - ), - ios: IOSInAppWebViewOptions( - enableViewportScale: false, - allowsLinkPreview: false - ), - ), - onLoadStart: (controller, uri) { - if (widget.onLoadStart != null) { - widget.onLoadStart!(controller, uri); - } + return WebView( + key: ValueKey(htmlData), + javascriptMode: JavascriptMode.unrestricted, + backgroundColor: Colors.white, + onWebViewCreated: (controller) async { + _webViewController = controller; + developer.log('onWebViewCreated(): html: $htmlData', name: 'HtmlContentViewer'); + await controller.loadHtmlString(htmlData, baseUrl: null); + widget.onCreated?.call(controller); }, - onWebViewCreated: _onWebViewCreated, - onLoadStop: (controller, uri) async { - final scrollHeight = await _webViewController.evaluateJavascript(source: 'document.body.scrollHeight'); - if ((scrollHeight != null) && mounted && (scrollHeight + 30.0 > widget.minHeight)) { + onPageFinished: (url) async { + final scrollHeightText = await _webViewController.runJavascriptReturningResult('document.body.scrollHeight'); + final scrollHeight = double.tryParse(scrollHeightText); + developer.log('onPageFinished(): scrollHeightText: $scrollHeightText', name: 'HtmlContentViewer'); + final scrollWidthText = await _webViewController.runJavascriptReturningResult('document.body.scrollWidth'); + // var scrollWidth = double.tryParse(scrollWidthText); + developer.log('onPageFinished(): scrollWidthText: $scrollWidthText', name: 'HtmlContentViewer'); + if ((scrollHeight != null) && mounted) { + final scrollHeightWithBuffer = scrollHeight + 30.0; + + if (scrollHeightWithBuffer > widget.minHeight) { + setState(() { + _webViewHeight = scrollHeightWithBuffer; + _isLoading = false; + }); + } + } + if (mounted && _isLoading) { setState(() { - _documentHeight = (scrollHeight + 30.0); + _isLoading = false; }); } - setState(() { - loading = false; - }); - if (widget.onLoadStop != null) { - widget.onLoadStop!(controller, uri); - } }, - onScrollChanged: (controller, x, y) { - if (y != 0) { - controller.scrollTo(x: 0, y: 0); - } - }, - shouldOverrideUrlLoading: _shouldOverrideUrlLoading, + navigationDelegate: _onNavigation, gestureRecognizers: { Factory(() => LongPressGestureRecognizer()), }, ); } - void _onWebViewCreated(InAppWebViewController controller) { - _webViewController = controller; - if (widget.onCreated != null) { - widget.onCreated!(_webViewController); + FutureOr _onNavigation(NavigationRequest navigation) async { + developer.log('_onNavigation()', name: 'HtmlContentViewer'); + // for iOS / WKWebView necessary: + if (navigation.isForMainFrame && navigation.url == 'about:blank') { + return NavigationDecision.navigate; } - } - - Future _shouldOverrideUrlLoading( - InAppWebViewController controller, - NavigationAction request - ) async { - final requestUri = request.request.url!; + final requestUri = Uri.parse(navigation.url); final mailtoHandler = widget.mailtoDelegate; if (mailtoHandler != null && requestUri.isScheme('mailto')) { await mailtoHandler(requestUri); - return NavigationActionPolicy.CANCEL; + return NavigationDecision.prevent; } - final url = requestUri.toString(); - if (await launcher.canLaunch(url)) { - await launcher.launch(url); - return NavigationActionPolicy.CANCEL; - } else { - return NavigationActionPolicy.ALLOW; + final url = navigation.url; + final urlDelegate = widget.urlLauncherDelegate; + if (urlDelegate != null) { + final handled = await urlDelegate(url); + if (handled) { + return NavigationDecision.prevent; + } } + await launcher.launch(url); + return NavigationDecision.prevent; + } + + FutureOr _onNavigationOnWebBrowser(WebNavigationRequest navigation) async { + developer.log('_onNavigationOnWebBrowser()', name: 'HtmlContentViewer'); + final requestUri = Uri.parse(navigation.url); + final mailtoHandler = widget.mailtoDelegate; + if (mailtoHandler != null && requestUri.isScheme('mailto')) { + await mailtoHandler(requestUri); + return WebNavigationDecision.prevent; + } + final url = navigation.url; + final urlDelegate = widget.urlLauncherDelegate; + if (urlDelegate != null) { + final handled = await urlDelegate(url); + if (handled) { + return WebNavigationDecision.prevent; + } + } + await launcher.launch(url); + return WebNavigationDecision.prevent; } } \ No newline at end of file diff --git a/core/pubspec.yaml b/core/pubspec.yaml index 620d76b82..ddb5c6eea 100644 --- a/core/pubspec.yaml +++ b/core/pubspec.yaml @@ -61,17 +61,17 @@ dependencies: # getwidget getwidget: 2.0.4 - # flutter_inappwebview - flutter_inappwebview: - git: - url: https://github.com/robert-virkus/flutter_inappwebview.git + # webview_flutter + webview_flutter: 3.0.0 + # easy_web_view: Display html for web browser easy_web_view: git: url: https://github.com/dab246/easy_web_view.git + ref: easy_web_improvements # url_launcher - url_launcher: 6.0.12 + url_launcher: 6.0.17 dev_dependencies: flutter_test: