TF-292 Fix two scroll bars for medium sized screens
This commit is contained in:
@@ -45,6 +45,8 @@ export 'presentation/views/dialog/confirmation_dialog_builder.dart';
|
|||||||
export 'presentation/views/dialog/edit_text_dialog_builder.dart';
|
export 'presentation/views/dialog/edit_text_dialog_builder.dart';
|
||||||
export 'presentation/views/background/background_widget_builder.dart';
|
export 'presentation/views/background/background_widget_builder.dart';
|
||||||
export 'presentation/views/html_viewer/html_content_viewer_widget.dart';
|
export 'presentation/views/html_viewer/html_content_viewer_widget.dart';
|
||||||
|
export 'presentation/views/html_viewer/html_content_viewer_on_web_widget.dart';
|
||||||
|
export 'presentation/views/html_viewer/html_viewer_controller_for_web.dart';
|
||||||
export 'presentation/views/floating_button/scrolling_floating_button_animated.dart';
|
export 'presentation/views/floating_button/scrolling_floating_button_animated.dart';
|
||||||
export 'presentation/views/bottom_popup/cupertino_action_sheet_action_builder.dart';
|
export 'presentation/views/bottom_popup/cupertino_action_sheet_action_builder.dart';
|
||||||
export 'presentation/views/bottom_popup/cupertino_action_sheet_builder.dart';
|
export 'presentation/views/bottom_popup/cupertino_action_sheet_builder.dart';
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// Copyright 2019 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
/// This file shims dart:ui in web-only scenarios, getting rid of the need to
|
||||||
|
/// suppress analyzer warnings.
|
||||||
|
|
||||||
|
// TODO(): flutter/flutter#55000 Remove this file once web-only dart:ui APIs
|
||||||
|
// are exposed from a dedicated place.
|
||||||
|
export 'dart_ui_fake.dart' if (dart.library.html) 'dart_ui_real.dart';
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// Copyright 2019 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
/// Shim for web_ui engine.PlatformViewRegistry
|
||||||
|
/// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L62
|
||||||
|
// ignore: camel_case_types
|
||||||
|
class platformViewRegistry {
|
||||||
|
/// Shim for registerViewFactory
|
||||||
|
/// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L72
|
||||||
|
static void registerViewFactory(
|
||||||
|
String viewTypeId, dynamic Function(int viewId) viewFactory) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Signature of callbacks that have no arguments and return no data.
|
||||||
|
typedef VoidCallback = void Function();
|
||||||
|
|
||||||
|
dynamic get window => null;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// Copyright 2019 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
export 'dart:ui';
|
||||||
@@ -0,0 +1,232 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:core/presentation/extensions/color_extension.dart';
|
||||||
|
import 'package:core/presentation/views/html_viewer/html_viewer_controller_for_web.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'dart:developer' as developer;
|
||||||
|
import 'package:universal_html/html.dart' as html;
|
||||||
|
import 'package:core/presentation/utils/shims/dart_ui.dart' as ui;
|
||||||
|
|
||||||
|
class HtmlContentViewerOnWeb extends StatefulWidget {
|
||||||
|
|
||||||
|
final String contentHtml;
|
||||||
|
final double widthContent;
|
||||||
|
final HtmlViewerControllerForWeb controller;
|
||||||
|
|
||||||
|
const HtmlContentViewerOnWeb({
|
||||||
|
Key? key,
|
||||||
|
required this.contentHtml,
|
||||||
|
required this.widthContent,
|
||||||
|
required this.controller,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_HtmlContentViewerOnWebState createState() => _HtmlContentViewerOnWebState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _HtmlContentViewerOnWebState extends State<HtmlContentViewerOnWeb> {
|
||||||
|
|
||||||
|
/// The view ID for the IFrameElement. Must be unique.
|
||||||
|
late String createdViewId;
|
||||||
|
/// The actual height of the editor, used to automatically set the height
|
||||||
|
late double actualHeight;
|
||||||
|
|
||||||
|
Future<bool>? webInit;
|
||||||
|
String? _htmlData;
|
||||||
|
bool _isLoading = true;
|
||||||
|
int minHeight = 100;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
actualHeight = 400;
|
||||||
|
createdViewId = _getRandString(10);
|
||||||
|
widget.controller.viewId = createdViewId;
|
||||||
|
_setUpWeb();
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getRandString(int len) {
|
||||||
|
var random = Random.secure();
|
||||||
|
var values = List<int>.generate(len, (i) => random.nextInt(255));
|
||||||
|
return base64UrlEncode(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
String _generateHtmlDocument(String content) {
|
||||||
|
final htmlScripts = '''
|
||||||
|
<script type="text/javascript">
|
||||||
|
window.parent.addEventListener('message', handleMessage, false);
|
||||||
|
|
||||||
|
function handleMessage(e) {
|
||||||
|
if (e && e.data && e.data.includes("toIframe:")) {
|
||||||
|
var data = JSON.parse(e.data);
|
||||||
|
if (data["view"].includes("$createdViewId")) {
|
||||||
|
if (data["type"].includes("getHeight")) {
|
||||||
|
var height = document.body.scrollHeight;
|
||||||
|
window.parent.postMessage(JSON.stringify({"view": "$createdViewId", "type": "toDart: htmlHeight", "height": height}), "*");
|
||||||
|
}
|
||||||
|
if (data["type"].includes("execCommand")) {
|
||||||
|
if (data["argument"] === null) {
|
||||||
|
document.execCommand(data["command"], false);
|
||||||
|
} else {
|
||||||
|
document.execCommand(data["command"], false, data["argument"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
''';
|
||||||
|
|
||||||
|
final htmlTemplate = '''
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
|
<style>
|
||||||
|
#editor {
|
||||||
|
outline: 0px solid transparent;
|
||||||
|
min-height: ${minHeight}px;
|
||||||
|
min-width: 300px;
|
||||||
|
color: #000000;
|
||||||
|
font-family: verdana;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
padding: 13px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
padding: 13px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
$htmlScripts
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="editor">$content</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
''';
|
||||||
|
|
||||||
|
return htmlTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setUpWeb() {
|
||||||
|
_htmlData = _generateHtmlDocument(widget.contentHtml);
|
||||||
|
|
||||||
|
final iframe = html.IFrameElement()
|
||||||
|
..width = widget.widthContent.toString()
|
||||||
|
..height = actualHeight.toString()
|
||||||
|
..srcdoc = _htmlData ?? ''
|
||||||
|
..style.border = 'none'
|
||||||
|
..style.overflowY = 'hidden'
|
||||||
|
..onLoad.listen((event) async {
|
||||||
|
var data = <String, Object>{'type': 'toIframe: getHeight'};
|
||||||
|
data['view'] = createdViewId;
|
||||||
|
final jsonEncoder = JsonEncoder();
|
||||||
|
var jsonStr = jsonEncoder.convert(data);
|
||||||
|
html.window.postMessage(jsonStr, '*');
|
||||||
|
|
||||||
|
html.window.onBeforeUnload.listen((event) {
|
||||||
|
event.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
html.window.onMessage.listen((event) {
|
||||||
|
var data = json.decode(event.data);
|
||||||
|
developer.log('onMessage(): data: $data', name: 'HtmlContentViewerOnWeb');
|
||||||
|
if (data['type'] != null &&
|
||||||
|
data['type'].contains('toDart: htmlHeight') &&
|
||||||
|
data['view'] == createdViewId) {
|
||||||
|
final docHeight = data['height'] ?? actualHeight;
|
||||||
|
developer.log('onMessage(): docHeight: $docHeight', name: 'HtmlContentViewerOnWeb');
|
||||||
|
if (docHeight != null && mounted) {
|
||||||
|
final scrollHeightWithBuffer = docHeight + 30.0;
|
||||||
|
if (scrollHeightWithBuffer > minHeight) {
|
||||||
|
setState(() {
|
||||||
|
actualHeight = scrollHeightWithBuffer;
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mounted && _isLoading) {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data['type'] != null &&
|
||||||
|
data['type'].contains('toDart: onChangeContent') &&
|
||||||
|
data['view'] == createdViewId) {
|
||||||
|
if (Scrollable.of(context) != null) {
|
||||||
|
Scrollable.of(context)!.position.ensureVisible(
|
||||||
|
context.findRenderObject()!,
|
||||||
|
duration: const Duration(milliseconds: 100),
|
||||||
|
curve: Curves.easeIn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.platformViewRegistry.registerViewFactory(createdViewId, (int viewId) => iframe);
|
||||||
|
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
webInit = Future.value(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Stack(
|
||||||
|
alignment: AlignmentDirectional.center,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: actualHeight,
|
||||||
|
width: widget.widthContent,
|
||||||
|
child: _buildWebView(),
|
||||||
|
),
|
||||||
|
if (_isLoading) _buildLoadingView()
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildLoadingView() {
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
child: SizedBox(
|
||||||
|
width: 30,
|
||||||
|
height: 30,
|
||||||
|
child: CircularProgressIndicator(color: AppColor.colorTextButton)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildWebView() {
|
||||||
|
final htmlData = _htmlData;
|
||||||
|
if (htmlData == null || htmlData.isEmpty) {
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: FutureBuilder<bool>(
|
||||||
|
future: webInit,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.hasData) {
|
||||||
|
return HtmlElementView(
|
||||||
|
key: ValueKey(htmlData),
|
||||||
|
viewType: createdViewId,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:core/core.dart';
|
import 'package:core/core.dart';
|
||||||
import 'package:easy_web_view/easy_web_view.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -12,9 +11,7 @@ import 'dart:developer' as developer;
|
|||||||
class HtmlContentViewer extends StatefulWidget {
|
class HtmlContentViewer extends StatefulWidget {
|
||||||
|
|
||||||
final String contentHtml;
|
final String contentHtml;
|
||||||
final int minHeight;
|
|
||||||
final double widthContent;
|
final double widthContent;
|
||||||
final double? heightContent;
|
|
||||||
final Widget? loadingWidget;
|
final Widget? loadingWidget;
|
||||||
|
|
||||||
/// Register this callback if you want a reference to the [WebViewController].
|
/// Register this callback if you want a reference to the [WebViewController].
|
||||||
@@ -32,8 +29,6 @@ class HtmlContentViewer extends StatefulWidget {
|
|||||||
Key? key,
|
Key? key,
|
||||||
required this.contentHtml,
|
required this.contentHtml,
|
||||||
required this.widthContent,
|
required this.widthContent,
|
||||||
this.heightContent,
|
|
||||||
this.minHeight = 100,
|
|
||||||
this.loadingWidget,
|
this.loadingWidget,
|
||||||
this.onCreated,
|
this.onCreated,
|
||||||
this.urlLauncherDelegate,
|
this.urlLauncherDelegate,
|
||||||
@@ -48,9 +43,9 @@ class _HtmlContentViewState extends State<HtmlContentViewer> {
|
|||||||
|
|
||||||
double? _webViewHeight = 1.0;
|
double? _webViewHeight = 1.0;
|
||||||
double? _webViewWidth = 1.0;
|
double? _webViewWidth = 1.0;
|
||||||
|
int minHeight = 100;
|
||||||
String? _htmlData;
|
String? _htmlData;
|
||||||
late WebViewController _webViewController;
|
late WebViewController _webViewController;
|
||||||
// late EasyWebViewControllerWrapperBase _easyWebViewControllerWrapperBase;
|
|
||||||
bool _isLoading = true;
|
bool _isLoading = true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -69,7 +64,7 @@ class _HtmlContentViewState extends State<HtmlContentViewer> {
|
|||||||
<style>
|
<style>
|
||||||
#editor {
|
#editor {
|
||||||
outline: 0px solid transparent;
|
outline: 0px solid transparent;
|
||||||
min-height: ${widget.minHeight}px;
|
min-height: ${minHeight}px;
|
||||||
min-width: 300px;
|
min-width: 300px;
|
||||||
color: #182952;
|
color: #182952;
|
||||||
font-family: verdana;
|
font-family: verdana;
|
||||||
@@ -106,28 +101,18 @@ class _HtmlContentViewState extends State<HtmlContentViewer> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (kIsWeb) {
|
_webViewWidth = widget.widthContent;
|
||||||
return Stack(
|
return Stack(
|
||||||
alignment: AlignmentDirectional.center,
|
alignment: AlignmentDirectional.center,
|
||||||
children: [
|
children: [
|
||||||
_buildWebViewOnBrowser(),
|
SizedBox(
|
||||||
if (_isLoading) _buildLoadingView()
|
height: _webViewHeight,
|
||||||
],
|
width: _webViewWidth,
|
||||||
);
|
child: _buildWebView(),
|
||||||
} else {
|
),
|
||||||
_webViewWidth = widget.widthContent;
|
if (_isLoading) _buildLoadingView()
|
||||||
return Stack(
|
],
|
||||||
alignment: AlignmentDirectional.center,
|
);
|
||||||
children: [
|
|
||||||
SizedBox(
|
|
||||||
height: _webViewHeight,
|
|
||||||
width: _webViewWidth,
|
|
||||||
child: _buildWebView(),
|
|
||||||
),
|
|
||||||
if (_isLoading) _buildLoadingView()
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildLoadingView() {
|
Widget _buildLoadingView() {
|
||||||
@@ -143,28 +128,6 @@ class _HtmlContentViewState extends State<HtmlContentViewer> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildWebViewOnBrowser() {
|
|
||||||
final htmlData = _htmlData;
|
|
||||||
if (htmlData == null || htmlData.isEmpty) {
|
|
||||||
return Container();
|
|
||||||
}
|
|
||||||
return EasyWebView(
|
|
||||||
key: ValueKey(htmlData),
|
|
||||||
src: htmlData,
|
|
||||||
onLoaded: (controller) async {
|
|
||||||
// _easyWebViewControllerWrapperBase = controller;
|
|
||||||
if (mounted && _isLoading) {
|
|
||||||
setState(() {
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
isHtml: true,
|
|
||||||
height: widget.heightContent,
|
|
||||||
webNavigationDelegate: _onNavigationOnWebBrowser,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildWebView() {
|
Widget _buildWebView() {
|
||||||
final htmlData = _htmlData;
|
final htmlData = _htmlData;
|
||||||
if (htmlData == null || htmlData.isEmpty) {
|
if (htmlData == null || htmlData.isEmpty) {
|
||||||
@@ -176,7 +139,6 @@ class _HtmlContentViewState extends State<HtmlContentViewer> {
|
|||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
onWebViewCreated: (controller) async {
|
onWebViewCreated: (controller) async {
|
||||||
_webViewController = controller;
|
_webViewController = controller;
|
||||||
developer.log('onWebViewCreated(): html: $htmlData', name: 'HtmlContentViewer');
|
|
||||||
await controller.loadHtmlString(htmlData, baseUrl: null);
|
await controller.loadHtmlString(htmlData, baseUrl: null);
|
||||||
widget.onCreated?.call(controller);
|
widget.onCreated?.call(controller);
|
||||||
},
|
},
|
||||||
@@ -184,13 +146,9 @@ class _HtmlContentViewState extends State<HtmlContentViewer> {
|
|||||||
final scrollHeightText = await _webViewController.runJavascriptReturningResult('document.body.scrollHeight');
|
final scrollHeightText = await _webViewController.runJavascriptReturningResult('document.body.scrollHeight');
|
||||||
final scrollHeight = double.tryParse(scrollHeightText);
|
final scrollHeight = double.tryParse(scrollHeightText);
|
||||||
developer.log('onPageFinished(): scrollHeightText: $scrollHeightText', name: 'HtmlContentViewer');
|
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) {
|
if ((scrollHeight != null) && mounted) {
|
||||||
final scrollHeightWithBuffer = scrollHeight + 30.0;
|
final scrollHeightWithBuffer = scrollHeight + 30.0;
|
||||||
|
if (scrollHeightWithBuffer > minHeight) {
|
||||||
if (scrollHeightWithBuffer > widget.minHeight) {
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_webViewHeight = scrollHeightWithBuffer;
|
_webViewHeight = scrollHeightWithBuffer;
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
@@ -211,8 +169,6 @@ class _HtmlContentViewState extends State<HtmlContentViewer> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<NavigationDecision> _onNavigation(NavigationRequest navigation) async {
|
FutureOr<NavigationDecision> _onNavigation(NavigationRequest navigation) async {
|
||||||
developer.log('_onNavigation()', name: 'HtmlContentViewer');
|
|
||||||
// for iOS / WKWebView necessary:
|
|
||||||
if (navigation.isForMainFrame && navigation.url == 'about:blank') {
|
if (navigation.isForMainFrame && navigation.url == 'about:blank') {
|
||||||
return NavigationDecision.navigate;
|
return NavigationDecision.navigate;
|
||||||
}
|
}
|
||||||
@@ -233,24 +189,4 @@ class _HtmlContentViewState extends State<HtmlContentViewer> {
|
|||||||
await launcher.launch(url);
|
await launcher.launch(url);
|
||||||
return NavigationDecision.prevent;
|
return NavigationDecision.prevent;
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<WebNavigationDecision> _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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'package:universal_html/html.dart' as html;
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
class HtmlViewerControllerForWeb {
|
||||||
|
|
||||||
|
HtmlViewerControllerForWeb();
|
||||||
|
|
||||||
|
/// Manages the view ID for the [HtmlViewerControllerForWeb] on web
|
||||||
|
String? _viewId;
|
||||||
|
|
||||||
|
set viewId(String? viewId) => _viewId = viewId;
|
||||||
|
|
||||||
|
/// Recalculates the height of the editor to remove any vertical scrolling.
|
||||||
|
void recalculateHeight() {
|
||||||
|
_evaluateJavascriptWeb(data: {
|
||||||
|
'type': 'toIframe: getHeight',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A function to quickly call a document.execCommand function in a readable format
|
||||||
|
void execCommand(String command, {String? argument}) {
|
||||||
|
_evaluateJavascriptWeb(data: {
|
||||||
|
'type': 'toIframe: execCommand',
|
||||||
|
'command': command,
|
||||||
|
'argument': argument
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A function to execute JS passed as a [WebScript] to the editor. This should
|
||||||
|
/// only be used on Flutter Web.
|
||||||
|
Future<dynamic> evaluateJavascriptWeb(String name,
|
||||||
|
{bool hasReturnValue = false}) async {
|
||||||
|
_evaluateJavascriptWeb(data: {'type': 'toIframe: $name'});
|
||||||
|
if (hasReturnValue) {
|
||||||
|
var e = await html.window.onMessage.firstWhere(
|
||||||
|
(element) => json.decode(element.data)['type'] == 'toDart: $name');
|
||||||
|
return json.decode(e.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function to run javascript and check current environment
|
||||||
|
void _evaluateJavascriptWeb({required Map<String, Object?> data}) async {
|
||||||
|
if (kIsWeb) {
|
||||||
|
data['view'] = _viewId;
|
||||||
|
final jsonEncoder = JsonEncoder();
|
||||||
|
var json = jsonEncoder.convert(data);
|
||||||
|
html.window.postMessage(json, '*');
|
||||||
|
} else {
|
||||||
|
throw Exception('Non-Flutter Web environment detected');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-6
@@ -64,12 +64,6 @@ dependencies:
|
|||||||
# webview_flutter
|
# webview_flutter
|
||||||
webview_flutter: 3.0.0
|
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
|
||||||
url_launcher: 6.0.17
|
url_launcher: 6.0.17
|
||||||
|
|
||||||
@@ -78,6 +72,8 @@ dependencies:
|
|||||||
|
|
||||||
universal_html: 2.0.8
|
universal_html: 2.0.8
|
||||||
|
|
||||||
|
http: 0.13.4
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
import 'package:core/core.dart';
|
import 'package:core/core.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:model/model.dart';
|
import 'package:model/model.dart';
|
||||||
|
|
||||||
@@ -32,12 +33,17 @@ class EmailContentItemBuilder {
|
|||||||
Widget _buildItem() {
|
Widget _buildItem() {
|
||||||
switch(_emailContent.type) {
|
switch(_emailContent.type) {
|
||||||
case EmailContentType.textHtml:
|
case EmailContentType.textHtml:
|
||||||
return HtmlContentViewer(
|
if (kIsWeb) {
|
||||||
widthContent: MediaQuery.of(_context).size.width,
|
return HtmlContentViewerOnWeb(
|
||||||
heightContent: MediaQuery.of(_context).size.height,
|
widthContent: MediaQuery.of(_context).size.width,
|
||||||
contentHtml: _emailContent.content,
|
contentHtml: _emailContent.content,
|
||||||
loadingWidget: loadingWidget,
|
controller: HtmlViewerControllerForWeb());
|
||||||
mailtoDelegate: (uri) async {});
|
} else {
|
||||||
|
return HtmlContentViewer(
|
||||||
|
widthContent: MediaQuery.of(_context).size.width,
|
||||||
|
contentHtml: _emailContent.content,
|
||||||
|
loadingWidget: loadingWidget);
|
||||||
|
}
|
||||||
case EmailContentType.textPlain:
|
case EmailContentType.textPlain:
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
|
|||||||
Reference in New Issue
Block a user