diff --git a/example/lib/generated_plugin_registrant.dart b/example/lib/generated_plugin_registrant.dart new file mode 100644 index 0000000000..f404d1670d --- /dev/null +++ b/example/lib/generated_plugin_registrant.dart @@ -0,0 +1,17 @@ +// +// Generated file. Do not edit. +// + +// ignore_for_file: lines_longer_than_80_chars + +import 'package:video_player_web/video_player_web.dart'; +import 'package:wakelock_web/wakelock_web.dart'; + +import 'package:flutter_web_plugins/flutter_web_plugins.dart'; + +// ignore: public_member_api_docs +void registerPlugins(Registrar registrar) { + VideoPlayerPlugin.registerWith(registrar); + WakelockWeb.registerWith(registrar); + registrar.registerMessageHandler(); +} diff --git a/example/lib/main.dart b/example/lib/main.dart index 5d8d5b75c5..85f6e9cf85 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -167,10 +167,10 @@ class _MyHomePageState extends State { // Custom placeholder image for broken links networkSourceMatcher(): networkImageRender(altWidget: (_) => FlutterLogo()), }, - onLinkTap: (url) { + onLinkTap: (url, _, __, ___) { print("Opening $url..."); }, - onImageTap: (src) { + onImageTap: (src, _, __, ___) { print(src); }, onImageError: (exception, stackTrace) { diff --git a/example/web/favicon.png b/example/web/favicon.png new file mode 100644 index 0000000000..8aaa46ac1a Binary files /dev/null and b/example/web/favicon.png differ diff --git a/example/web/icons/Icon-192.png b/example/web/icons/Icon-192.png new file mode 100644 index 0000000000..b749bfef07 Binary files /dev/null and b/example/web/icons/Icon-192.png differ diff --git a/example/web/icons/Icon-512.png b/example/web/icons/Icon-512.png new file mode 100644 index 0000000000..88cfd48dff Binary files /dev/null and b/example/web/icons/Icon-512.png differ diff --git a/example/web/index.html b/example/web/index.html new file mode 100644 index 0000000000..1460b5e9ba --- /dev/null +++ b/example/web/index.html @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + example + + + + + + + + diff --git a/example/web/manifest.json b/example/web/manifest.json new file mode 100644 index 0000000000..8c012917da --- /dev/null +++ b/example/web/manifest.json @@ -0,0 +1,23 @@ +{ + "name": "example", + "short_name": "example", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "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" + } + ] +} diff --git a/lib/src/replaced_element.dart b/lib/src/replaced_element.dart index 35b5b14a24..95da181ee8 100644 --- a/lib/src/replaced_element.dart +++ b/lib/src/replaced_element.dart @@ -3,12 +3,14 @@ import 'dart:math'; import 'package:chewie/chewie.dart'; import 'package:chewie_audio/chewie_audio.dart'; import 'package:flutter/foundation.dart'; -import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_html/html_parser.dart'; import 'package:flutter_html/src/html_elements.dart'; import 'package:flutter_html/src/utils.dart'; +import 'package:flutter_html/src/widgets/iframe_unsupported.dart' + if (dart.library.io) 'package:flutter_html/src/widgets/iframe_mobile.dart' + if (dart.library.html) 'package:flutter_html/src/widgets/iframe_web.dart'; import 'package:flutter_html/style.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:html/dom.dart' as dom; @@ -100,45 +102,6 @@ class ImageContentElement extends ReplacedElement { } } -/// [IframeContentElement is a [ReplacedElement] with web content. -class IframeContentElement extends ReplacedElement { - final String src; - final double width; - final double height; - final NavigationDelegate navigationDelegate; - final UniqueKey key = UniqueKey(); - - IframeContentElement({ - String name, - Style style, - this.src, - this.width, - this.height, - dom.Element node, - this.navigationDelegate, - }) : super(name: name, style: style, node: node); - - @override - Widget toWidget(RenderContext context) { - final sandboxMode = attributes["sandbox"]; - return Container( - width: width ?? (height ?? 150) * 2, - height: height ?? (width ?? 300) / 2, - child: WebView( - initialUrl: src, - key: key, - javascriptMode: sandboxMode == null || sandboxMode == "allow-scripts" - ? JavascriptMode.unrestricted - : JavascriptMode.disabled, - navigationDelegate: navigationDelegate, - gestureRecognizers: { - Factory(() => VerticalDragGestureRecognizer()) - }, - ), - ); - } -} - /// [AudioContentElement] is a [ContentElement] with an audio file as its content. class AudioContentElement extends ReplacedElement { final List src; diff --git a/lib/src/utils.dart b/lib/src/utils.dart index fddb87d88e..90b8a9ad21 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -1,5 +1,8 @@ import 'package:flutter/gestures.dart'; +import 'dart:math'; +import 'dart:convert'; + class Context { T data; @@ -43,3 +46,9 @@ class MultipleTapGestureRecognizer extends TapGestureRecognizer { } } } + +String getRandString(int len) { + var random = Random.secure(); + var values = List.generate(len, (i) => random.nextInt(255)); + return base64UrlEncode(values); +} \ No newline at end of file diff --git a/lib/src/widgets/iframe_mobile.dart b/lib/src/widgets/iframe_mobile.dart new file mode 100644 index 0000000000..13a67513d9 --- /dev/null +++ b/lib/src/widgets/iframe_mobile.dart @@ -0,0 +1,47 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_html/html_parser.dart'; +import 'package:flutter_html/src/replaced_element.dart'; +import 'package:flutter_html/style.dart'; +import 'package:webview_flutter/webview_flutter.dart'; +import 'package:html/dom.dart' as dom; + +/// [IframeContentElement is a [ReplacedElement] with web content. +class IframeContentElement extends ReplacedElement { + final String src; + final double width; + final double height; + final NavigationDelegate navigationDelegate; + final UniqueKey key = UniqueKey(); + + IframeContentElement({ + String name, + Style style, + this.src, + this.width, + this.height, + dom.Element node, + this.navigationDelegate, + }) : super(name: name, style: style, node: node); + + @override + Widget toWidget(RenderContext context) { + final sandboxMode = attributes["sandbox"]; + return Container( + width: width ?? (height ?? 150) * 2, + height: height ?? (width ?? 300) / 2, + child: WebView( + initialUrl: src, + key: key, + javascriptMode: sandboxMode == null || sandboxMode == "allow-scripts" + ? JavascriptMode.unrestricted + : JavascriptMode.disabled, + navigationDelegate: navigationDelegate, + gestureRecognizers: { + Factory(() => VerticalDragGestureRecognizer()) + }, + ), + ); + } +} \ No newline at end of file diff --git a/lib/src/widgets/iframe_unsupported.dart b/lib/src/widgets/iframe_unsupported.dart new file mode 100644 index 0000000000..35c72d2388 --- /dev/null +++ b/lib/src/widgets/iframe_unsupported.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_html/html_parser.dart'; +import 'package:flutter_html/src/replaced_element.dart'; +import 'package:flutter_html/style.dart'; +import 'package:webview_flutter/webview_flutter.dart'; +import 'package:html/dom.dart' as dom; + +/// [IframeContentElement is a [ReplacedElement] with web content. +class IframeContentElement extends ReplacedElement { + final String src; + final double width; + final double height; + final NavigationDelegate navigationDelegate; + final UniqueKey key = UniqueKey(); + + IframeContentElement({ + String name, + Style style, + this.src, + this.width, + this.height, + dom.Element node, + this.navigationDelegate, + }) : super(name: name, style: style, node: node); + + @override + Widget toWidget(RenderContext context) { + return Container( + width: width ?? (height ?? 150) * 2, + height: height ?? (width ?? 300) / 2, + child: Text("Iframes are currently not supported in this environment"), + ); + } +} \ No newline at end of file diff --git a/lib/src/widgets/iframe_web.dart b/lib/src/widgets/iframe_web.dart new file mode 100644 index 0000000000..59eb8ee892 --- /dev/null +++ b/lib/src/widgets/iframe_web.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_html/html_parser.dart'; +import 'package:flutter_html/src/replaced_element.dart'; +import 'package:flutter_html/src/utils.dart'; +import 'package:flutter_html/style.dart'; +import 'package:webview_flutter/webview_flutter.dart'; +import 'package:html/dom.dart' as dom; +// ignore: avoid_web_libraries_in_flutter +import 'dart:html' as html; +import 'dart:ui' as ui; + +/// [IframeContentElement is a [ReplacedElement] with web content. +class IframeContentElement extends ReplacedElement { + final String src; + final double width; + final double height; + final NavigationDelegate navigationDelegate; + final UniqueKey key = UniqueKey(); + final String createdViewId = getRandString(10); + + IframeContentElement({ + String name, + Style style, + this.src, + this.width, + this.height, + dom.Element node, + this.navigationDelegate, + }) : super(name: name, style: style, node: node); + + @override + Widget toWidget(RenderContext context) { + final html.IFrameElement iframe = html.IFrameElement() + ..width = (width ?? (height ?? 150) * 2).toString() + ..height = (height ?? (width ?? 300) / 2).toString() + ..src = src + ..style.border = 'none'; + //not actually an error + ui.platformViewRegistry.registerViewFactory(createdViewId, (int viewId) => iframe); + return Container( + width: width ?? (height ?? 150) * 2, + height: height ?? (width ?? 300) / 2, + child: Directionality( + textDirection: TextDirection.ltr, + child: HtmlElementView( + viewType: createdViewId, + ) + ) + ); + } +} \ No newline at end of file