From 3958204fa6f7fc3839867641b95a43cdce19f1cb Mon Sep 17 00:00:00 2001 From: tanay Date: Sun, 9 May 2021 08:16:26 -0400 Subject: [PATCH 01/10] Create packages for external dependency tags, delete unnecessary code in core, & update example --- example/lib/main.dart | 14 +- example/pubspec.yaml | 2 + lib/flutter_html.dart | 16 +- lib/html_parser.dart | 12 +- lib/image_render.dart | 52 +--- lib/src/html_elements.dart | 8 +- lib/src/layout_element.dart | 150 +--------- lib/src/replaced_element.dart | 266 ------------------ lib/src/utils.dart | 26 -- lib/src/widgets/iframe_mobile.dart | 46 --- lib/src/widgets/iframe_unsupported.dart | 33 --- lib/src/widgets/iframe_web.dart | 50 ---- packages/flutter_html_all/.gitignore | 75 +++++ packages/flutter_html_all/.metadata | 10 + packages/flutter_html_all/CHANGELOG.md | 3 + packages/flutter_html_all/LICENSE | 1 + packages/flutter_html_all/README.md | 14 + .../lib/flutter_html_all.dart | 8 + packages/flutter_html_all/pubspec.yaml | 66 +++++ packages/flutter_html_audio/.gitignore | 75 +++++ packages/flutter_html_audio/.metadata | 10 + packages/flutter_html_audio/CHANGELOG.md | 3 + packages/flutter_html_audio/LICENSE | 1 + packages/flutter_html_audio/README.md | 14 + .../lib/flutter_html_audio.dart | 37 +++ packages/flutter_html_audio/pubspec.yaml | 58 ++++ packages/flutter_html_iframe/.gitignore | 75 +++++ packages/flutter_html_iframe/.metadata | 10 + packages/flutter_html_iframe/CHANGELOG.md | 3 + packages/flutter_html_iframe/LICENSE | 1 + packages/flutter_html_iframe/README.md | 14 + .../lib/flutter_html_iframe.dart | 11 + .../lib/iframe_mobile.dart | 27 ++ .../lib/iframe_unsupported.dart | 9 + .../flutter_html_iframe/lib/iframe_web.dart | 40 +++ packages/flutter_html_iframe/pubspec.yaml | 57 ++++ packages/flutter_html_math/.gitignore | 75 +++++ packages/flutter_html_math/.metadata | 10 + packages/flutter_html_math/CHANGELOG.md | 3 + packages/flutter_html_math/LICENSE | 1 + packages/flutter_html_math/README.md | 14 + .../lib/flutter_html_math.dart | 96 +++++++ packages/flutter_html_math/pubspec.yaml | 57 ++++ packages/flutter_html_svg/.gitignore | 75 +++++ packages/flutter_html_svg/.metadata | 10 + packages/flutter_html_svg/CHANGELOG.md | 3 + packages/flutter_html_svg/LICENSE | 1 + packages/flutter_html_svg/README.md | 14 + .../lib/flutter_html_svg.dart | 101 +++++++ packages/flutter_html_svg/pubspec.yaml | 57 ++++ packages/flutter_html_table/.gitignore | 75 +++++ packages/flutter_html_table/.metadata | 10 + packages/flutter_html_table/CHANGELOG.md | 3 + packages/flutter_html_table/LICENSE | 1 + packages/flutter_html_table/README.md | 14 + .../lib/flutter_html_table.dart | 137 +++++++++ packages/flutter_html_table/pubspec.yaml | 57 ++++ packages/flutter_html_video/.gitignore | 75 +++++ packages/flutter_html_video/.metadata | 10 + packages/flutter_html_video/CHANGELOG.md | 3 + packages/flutter_html_video/LICENSE | 1 + packages/flutter_html_video/README.md | 14 + .../lib/flutter_html_video.dart | 45 +++ packages/flutter_html_video/pubspec.yaml | 58 ++++ pubspec.yaml | 23 +- test/html_parser_test.dart | 4 +- 66 files changed, 1680 insertions(+), 664 deletions(-) delete mode 100644 lib/src/widgets/iframe_mobile.dart delete mode 100644 lib/src/widgets/iframe_unsupported.dart delete mode 100644 lib/src/widgets/iframe_web.dart create mode 100644 packages/flutter_html_all/.gitignore create mode 100644 packages/flutter_html_all/.metadata create mode 100644 packages/flutter_html_all/CHANGELOG.md create mode 100644 packages/flutter_html_all/LICENSE create mode 100644 packages/flutter_html_all/README.md create mode 100644 packages/flutter_html_all/lib/flutter_html_all.dart create mode 100644 packages/flutter_html_all/pubspec.yaml create mode 100644 packages/flutter_html_audio/.gitignore create mode 100644 packages/flutter_html_audio/.metadata create mode 100644 packages/flutter_html_audio/CHANGELOG.md create mode 100644 packages/flutter_html_audio/LICENSE create mode 100644 packages/flutter_html_audio/README.md create mode 100644 packages/flutter_html_audio/lib/flutter_html_audio.dart create mode 100644 packages/flutter_html_audio/pubspec.yaml create mode 100644 packages/flutter_html_iframe/.gitignore create mode 100644 packages/flutter_html_iframe/.metadata create mode 100644 packages/flutter_html_iframe/CHANGELOG.md create mode 100644 packages/flutter_html_iframe/LICENSE create mode 100644 packages/flutter_html_iframe/README.md create mode 100644 packages/flutter_html_iframe/lib/flutter_html_iframe.dart create mode 100644 packages/flutter_html_iframe/lib/iframe_mobile.dart create mode 100644 packages/flutter_html_iframe/lib/iframe_unsupported.dart create mode 100644 packages/flutter_html_iframe/lib/iframe_web.dart create mode 100644 packages/flutter_html_iframe/pubspec.yaml create mode 100644 packages/flutter_html_math/.gitignore create mode 100644 packages/flutter_html_math/.metadata create mode 100644 packages/flutter_html_math/CHANGELOG.md create mode 100644 packages/flutter_html_math/LICENSE create mode 100644 packages/flutter_html_math/README.md create mode 100644 packages/flutter_html_math/lib/flutter_html_math.dart create mode 100644 packages/flutter_html_math/pubspec.yaml create mode 100644 packages/flutter_html_svg/.gitignore create mode 100644 packages/flutter_html_svg/.metadata create mode 100644 packages/flutter_html_svg/CHANGELOG.md create mode 100644 packages/flutter_html_svg/LICENSE create mode 100644 packages/flutter_html_svg/README.md create mode 100644 packages/flutter_html_svg/lib/flutter_html_svg.dart create mode 100644 packages/flutter_html_svg/pubspec.yaml create mode 100644 packages/flutter_html_table/.gitignore create mode 100644 packages/flutter_html_table/.metadata create mode 100644 packages/flutter_html_table/CHANGELOG.md create mode 100644 packages/flutter_html_table/LICENSE create mode 100644 packages/flutter_html_table/README.md create mode 100644 packages/flutter_html_table/lib/flutter_html_table.dart create mode 100644 packages/flutter_html_table/pubspec.yaml create mode 100644 packages/flutter_html_video/.gitignore create mode 100644 packages/flutter_html_video/.metadata create mode 100644 packages/flutter_html_video/CHANGELOG.md create mode 100644 packages/flutter_html_video/LICENSE create mode 100644 packages/flutter_html_video/README.md create mode 100644 packages/flutter_html_video/lib/flutter_html_video.dart create mode 100644 packages/flutter_html_video/pubspec.yaml diff --git a/example/lib/main.dart b/example/lib/main.dart index 5a036d3e35..65e48c90a0 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_html/flutter_html.dart'; +import 'package:flutter_html_all/flutter_html_all.dart'; import 'package:flutter_math_fork/flutter_math.dart'; void main() => runApp(new MyApp()); @@ -290,8 +291,17 @@ class _MyHomePageState extends State { )), tableMatcher(): CustomRender.fromWidget(widget: (context, buildChildren) => SingleChildScrollView( scrollDirection: Axis.horizontal, - child: (context.tree as TableLayoutElement).toWidget(context), + child: tableRender.widget!.call(context, buildChildren), )), + audioMatcher(): audioRender, + iframeMatcher(): iframeRender(), + mathMatcher(): mathRender, + svgTagMatcher(): svgTagRender, + svgDataUriMatcher(): svgDataImageRender, + svgAssetUriMatcher(): svgAssetImageRender, + svgNetworkSourceMatcher(): svgNetworkImageRender, + tableMatcher(): tableRender, + videoMatcher(): videoRender, }, customImageRenders: { networkSourceMatcher(domains: ["flutter.dev"]): @@ -328,8 +338,6 @@ class _MyHomePageState extends State { } } -CustomRenderMatcher tableMatcher() => (context) => context.tree.element?.localName == 'table'; - CustomRenderMatcher texMatcher() => (context) => context.tree.element?.localName == 'tex'; CustomRenderMatcher birdMatcher() => (context) => context.tree.element?.localName == 'bird'; diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 73ff399401..71d4263e1f 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -9,6 +9,8 @@ environment: dependencies: flutter_html: path: .. + flutter_html_all: + path: ../packages/flutter_html_all flutter: sdk: flutter diff --git a/lib/flutter_html.dart b/lib/flutter_html.dart index e01b908a77..0ab3c4fd76 100644 --- a/lib/flutter_html.dart +++ b/lib/flutter_html.dart @@ -12,6 +12,8 @@ export 'package:flutter_html/src/layout_element.dart'; export 'package:flutter_html/src/replaced_element.dart'; export 'package:flutter_html/src/styled_element.dart'; export 'package:flutter_html/src/interactable_element.dart'; +//export anchor for use in flutter_html_ packages +export 'package:flutter_html/src/anchor.dart'; import 'package:flutter/material.dart'; import 'package:flutter_html/custom_render.dart'; @@ -20,7 +22,6 @@ import 'package:flutter_html/html_parser.dart'; import 'package:flutter_html/image_render.dart'; import 'package:flutter_html/src/html_elements.dart'; import 'package:flutter_html/style.dart'; -import 'package:webview_flutter/webview_flutter.dart'; import 'package:html/dom.dart' as dom; class Html extends StatelessWidget { @@ -61,7 +62,6 @@ class Html extends StatelessWidget { this.onImageTap, this.tagsList = const [], this.style = const {}, - this.navigationDelegateForIframe, }) : document = null, assert (data != null), anchorKey = GlobalKey(), @@ -79,10 +79,9 @@ class Html extends StatelessWidget { this.onImageTap, this.tagsList = const [], this.style = const {}, - this.navigationDelegateForIframe, }) : data = null, assert(document != null), - anchorKey = GlobalKey(), + anchorKey = GlobalKey(), super(key: key); /// A unique key for this Html widget to ensure uniqueness of anchors @@ -126,17 +125,13 @@ class Html extends StatelessWidget { /// An API that allows you to override the default style for any HTML element final Map style; - /// Decides how to handle a specific navigation request in the WebView of an - /// Iframe. It's necessary to use the webview_flutter package inside the app - /// to use NavigationDelegate. - final NavigationDelegate? navigationDelegateForIframe; - static List get tags => new List.from(STYLED_ELEMENTS) ..addAll(INTERACTABLE_ELEMENTS) ..addAll(REPLACED_ELEMENTS) ..addAll(LAYOUT_ELEMENTS) ..addAll(TABLE_CELL_ELEMENTS) - ..addAll(TABLE_DEFINITION_ELEMENTS); + ..addAll(TABLE_DEFINITION_ELEMENTS) + ..addAll(EXTERNAL_ELEMENTS); @override Widget build(BuildContext context) { @@ -161,7 +156,6 @@ class Html extends StatelessWidget { ..addAll(customImageRenders) ..addAll(defaultImageRenders), tagsList: tagsList.isEmpty ? Html.tags : tagsList, - navigationDelegateForIframe: navigationDelegateForIframe, ), ); } diff --git a/lib/html_parser.dart b/lib/html_parser.dart index e07ae61825..945722b4c2 100644 --- a/lib/html_parser.dart +++ b/lib/html_parser.dart @@ -14,7 +14,6 @@ import 'package:flutter_html/src/utils.dart'; import 'package:flutter_html/style.dart'; import 'package:html/dom.dart' as dom; import 'package:html/parser.dart' as htmlparser; -import 'package:webview_flutter/webview_flutter.dart'; typedef OnTap = void Function( String? url, @@ -41,7 +40,6 @@ class HtmlParser extends StatelessWidget { final Map customRenders; final Map imageRenders; final List tagsList; - final NavigationDelegate? navigationDelegateForIframe; final OnTap? onAnchorTap; HtmlParser({ @@ -56,7 +54,6 @@ class HtmlParser extends StatelessWidget { required this.customRenders, required this.imageRenders, required this.tagsList, - required this.navigationDelegateForIframe, }): this.onAnchorTap = key != null ? _handleAnchorTap(key, onLinkTap): null, super(key: key); @override @@ -65,7 +62,6 @@ class HtmlParser extends StatelessWidget { htmlData, customRenders.keys.toList(), tagsList, - navigationDelegateForIframe, context, this, ); @@ -115,7 +111,6 @@ class HtmlParser extends StatelessWidget { dom.Document html, List customRenderMatchers, List tagsList, - NavigationDelegate? navigationDelegateForIframe, BuildContext context, HtmlParser parser, ) { @@ -131,7 +126,6 @@ class HtmlParser extends StatelessWidget { node, customRenderMatchers, tagsList, - navigationDelegateForIframe, context, parser, )); @@ -148,7 +142,6 @@ class HtmlParser extends StatelessWidget { dom.Node node, List customRenderMatchers, List tagsList, - NavigationDelegate? navigationDelegateForIframe, BuildContext context, HtmlParser parser, ) { @@ -159,7 +152,6 @@ class HtmlParser extends StatelessWidget { childNode, customRenderMatchers, tagsList, - navigationDelegateForIframe, context, parser, )); @@ -175,7 +167,7 @@ class HtmlParser extends StatelessWidget { } else if (INTERACTABLE_ELEMENTS.contains(node.localName)) { return parseInteractableElement(node, children); } else if (REPLACED_ELEMENTS.contains(node.localName)) { - return parseReplacedElement(node, navigationDelegateForIframe); + return parseReplacedElement(node); } else if (LAYOUT_ELEMENTS.contains(node.localName)) { return parseLayoutElement(node, children); } else if (TABLE_CELL_ELEMENTS.contains(node.localName)) { @@ -335,7 +327,7 @@ class HtmlParser extends StatelessWidget { wpc.data = false; } - if (tree is ImageContentElement || tree is SvgContentElement) { + if (tree is ImageContentElement) { wpc.data = false; } diff --git a/lib/image_render.dart b/lib/image_render.dart index bd70d92904..ad84593e23 100644 --- a/lib/image_render.dart +++ b/lib/image_render.dart @@ -3,7 +3,6 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_html/html_parser.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:html/dom.dart' as dom; typedef ImageSourceMatcher = bool Function( @@ -74,21 +73,17 @@ ImageRender assetImageRender({ }) => (context, attributes, element) { final assetPath = _src(attributes)!.replaceFirst('asset:', ''); - if (_src(attributes)!.endsWith(".svg")) { - return SvgPicture.asset(assetPath); - } else { - return Image.asset( - assetPath, - width: width ?? _width(attributes), - height: height ?? _height(attributes), - frameBuilder: (ctx, child, frame, _) { - if (frame == null) { - return Text(_alt(attributes) ?? "", style: context.style.generateTextStyle()); - } - return child; - }, - ); - } + return Image.asset( + assetPath, + width: width ?? _width(attributes), + height: height ?? _height(attributes), + frameBuilder: (ctx, child, frame, _) { + if (frame == null) { + return Text(_alt(attributes) ?? "", style: context.style.generateTextStyle()); + } + return child; + }, + ); }; ImageRender networkImageRender({ @@ -163,34 +158,9 @@ ImageRender networkImageRender({ ); }; -ImageRender svgDataImageRender() => (context, attributes, element) { - final dataUri = _dataUriFormat.firstMatch(_src(attributes)!); - final data = dataUri?.namedGroup('data'); - if (data == null) return null; - if (dataUri?.namedGroup('encoding') == ';base64') { - final decodedImage = base64.decode(data.trim()); - return SvgPicture.memory( - decodedImage, - width: _width(attributes), - height: _height(attributes), - ); - } - return SvgPicture.string(Uri.decodeFull(data)); - }; - -ImageRender svgNetworkImageRender() => (context, attributes, element) { - return SvgPicture.network( - attributes["src"]!, - width: _width(attributes), - height: _height(attributes), - ); - }; - final Map defaultImageRenders = { - dataUriMatcher(mime: 'image/svg+xml', encoding: null): svgDataImageRender(), dataUriMatcher(): base64ImageRender(), assetUriMatcher(): assetImageRender(), - networkSourceMatcher(extension: "svg"): svgNetworkImageRender(), networkSourceMatcher(): networkImageRender(), }; diff --git a/lib/src/html_elements.dart b/lib/src/html_elements.dart index 020f0ae345..25a363fe63 100644 --- a/lib/src/html_elements.dart +++ b/lib/src/html_elements.dart @@ -75,23 +75,17 @@ const INTERACTABLE_ELEMENTS = [ ]; const REPLACED_ELEMENTS = [ - "audio", "br", "head", - "iframe", "img", - "svg", "template", - "video", "rp", "rt", "ruby", - "math", ]; const LAYOUT_ELEMENTS = [ "details", - "table", "tr", "tbody", "tfoot", @@ -102,6 +96,8 @@ const TABLE_CELL_ELEMENTS = ["th", "td"]; const TABLE_DEFINITION_ELEMENTS = ["col", "colgroup"]; +const EXTERNAL_ELEMENTS = ["audio", "iframe", "math", "svg", "table", "video"]; + /** Here is a list of elements with planned support: a - i [x] diff --git a/lib/src/layout_element.dart b/lib/src/layout_element.dart index 4c12ed4c59..33093e7493 100644 --- a/lib/src/layout_element.dart +++ b/lib/src/layout_element.dart @@ -1,12 +1,9 @@ -import 'dart:math'; - import 'package:flutter/material.dart'; import 'package:flutter_html/html_parser.dart'; import 'package:flutter_html/src/anchor.dart'; import 'package:flutter_html/src/html_elements.dart'; import 'package:flutter_html/src/styled_element.dart'; import 'package:flutter_html/style.dart'; -import 'package:flutter_layout_grid/flutter_layout_grid.dart'; import 'package:html/dom.dart' as dom; /// A [LayoutElement] is an element that breaks the normal Inline flow of @@ -22,141 +19,6 @@ abstract class LayoutElement extends StyledElement { Widget? toWidget(RenderContext context); } -class TableLayoutElement extends LayoutElement { - TableLayoutElement({ - required String name, - required List children, - required dom.Element node, - }) : super(name: name, children: children, node: node, elementId: node.id); - - @override - Widget toWidget(RenderContext context) { - return Container( - key: AnchorKey.of(context.parser.key, this), - margin: style.margin, - padding: style.padding, - decoration: BoxDecoration( - color: style.backgroundColor, - border: style.border, - ), - width: style.width, - height: style.height, - child: LayoutBuilder(builder: (_, constraints) => _layoutCells(context, constraints)), - ); - } - - Widget _layoutCells(RenderContext context, BoxConstraints constraints) { - final rows = []; - List columnSizes = []; - for (var child in children) { - if (child is TableStyleElement) { - // Map tags to predetermined column track sizes - columnSizes = child.children - .where((c) => c.name == "col") - .map((c) { - final span = int.tryParse(c.attributes["span"] ?? "1") ?? 1; - final colWidth = c.attributes["width"]; - return List.generate(span, (index) { - if (colWidth != null && colWidth.endsWith("%")) { - if (!constraints.hasBoundedWidth) { - // In a horizontally unbounded container; always wrap content instead of applying flex - return IntrinsicContentTrackSize(); - } - final percentageSize = double.tryParse( - colWidth.substring(0, colWidth.length - 1)); - return percentageSize != null && !percentageSize.isNaN - ? FlexibleTrackSize(percentageSize * 0.01) - : IntrinsicContentTrackSize(); - } else if (colWidth != null) { - final fixedPxSize = double.tryParse(colWidth); - return fixedPxSize != null - ? FixedTrackSize(fixedPxSize) - : IntrinsicContentTrackSize(); - } else { - return IntrinsicContentTrackSize(); - } - }); - }) - .expand((element) => element) - .toList(growable: false); - } else if (child is TableSectionLayoutElement) { - rows.addAll(child.children.whereType()); - } else if (child is TableRowLayoutElement) { - rows.add(child); - } - } - - // All table rows have a height intrinsic to their (spanned) contents - final rowSizes = - List.generate(rows.length, (_) => IntrinsicContentTrackSize()); - - // Calculate column bounds - int columnMax = rows - .map((row) => row.children - .whereType() - .fold(0, (int value, child) => value + child.colspan)) - .fold(0, max); - - // Place the cells in the rows/columns - final cells = []; - final columnRowOffset = List.generate(columnMax + 1, (_) => 0); - int rowi = 0; - for (var row in rows) { - int columni = 0; - for (var child in row.children) { - while (columnRowOffset[columni] > 0) { - columnRowOffset[columni] = columnRowOffset[columni] - 1; - columni++; - } - if (child is TableCellElement) { - cells.add(GridPlacement( - child: Container( - width: double.infinity, - padding: child.style.padding ?? row.style.padding, - decoration: BoxDecoration( - color: child.style.backgroundColor ?? row.style.backgroundColor, - border: child.style.border ?? row.style.border, - ), - child: SizedBox.expand( - child: Container( - alignment: child.style.alignment ?? - style.alignment ?? - Alignment.centerLeft, - child: StyledText( - textSpan: context.parser.parseTree(context, child), - style: child.style, - renderContext: context, - ), - ), - ), - ), - columnStart: columni, - columnSpan: child.colspan, - rowStart: rowi, - rowSpan: child.rowspan, - )); - columnRowOffset[columni] = child.rowspan - 1; - columni += child.colspan; - } - } - rowi++; - } - - // Create column tracks (insofar there were no colgroups that already defined them) - List finalColumnSizes = columnSizes.take(columnMax).toList(); - finalColumnSizes += List.generate( - max(0, columnMax - finalColumnSizes.length), - (_) => IntrinsicContentTrackSize()); - - return LayoutGrid( - gridFit: GridFit.loose, - columnSizes: finalColumnSizes, - rowSizes: rowSizes, - children: cells, - ); - } -} - class TableSectionLayoutElement extends LayoutElement { TableSectionLayoutElement({ required String name, @@ -333,12 +195,6 @@ LayoutElement parseLayoutElement( children: children, elementList: element.children ); - case "table": - return TableLayoutElement( - name: element.localName!, - children: children, - node: element, - ); case "thead": case "tbody": case "tfoot": @@ -353,10 +209,6 @@ LayoutElement parseLayoutElement( node: element, ); default: - return TableLayoutElement( - children: children, - name: "[[No Name]]", - node: element - ); + return EmptyLayoutElement(name: "[[No Name]]"); } } diff --git a/lib/src/replaced_element.dart b/lib/src/replaced_element.dart index 0a6a6eaff9..c3561daeaf 100644 --- a/lib/src/replaced_element.dart +++ b/lib/src/replaced_element.dart @@ -1,22 +1,13 @@ import 'dart:math'; -import 'package:chewie/chewie.dart'; -import 'package:chewie_audio/chewie_audio.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_html/html_parser.dart'; import 'package:flutter_html/src/anchor.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_math_fork/flutter_math.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:html/dom.dart' as dom; -import 'package:video_player/video_player.dart'; -import 'package:webview_flutter/webview_flutter.dart'; /// A [ReplacedElement] is a type of [StyledElement] that does not require its [children] to be rendered. /// @@ -100,123 +91,6 @@ class ImageContentElement extends ReplacedElement { } } -/// [AudioContentElement] is a [ContentElement] with an audio file as its content. -class AudioContentElement extends ReplacedElement { - final List src; - final bool showControls; - final bool autoplay; - final bool loop; - final bool muted; - - AudioContentElement({ - required String name, - required this.src, - required this.showControls, - required this.autoplay, - required this.loop, - required this.muted, - required dom.Element node, - }) : super(name: name, style: Style(), node: node, elementId: node.id); - - @override - Widget toWidget(RenderContext context) { - return Container( - key: AnchorKey.of(context.parser.key, this), - width: context.style.width ?? 300, - height: Theme.of(context.buildContext).platform == TargetPlatform.android - ? 48 : 75, - child: ChewieAudio( - controller: ChewieAudioController( - videoPlayerController: VideoPlayerController.network( - src.first ?? "", - ), - autoPlay: autoplay, - looping: loop, - showControls: showControls, - autoInitialize: true, - ), - ), - ); - } -} - -/// [VideoContentElement] is a [ContentElement] with a video file as its content. -class VideoContentElement extends ReplacedElement { - final List src; - final String? poster; - final bool showControls; - final bool autoplay; - final bool loop; - final bool muted; - final double? width; - final double? height; - - VideoContentElement({ - required String name, - required this.src, - required this.poster, - required this.showControls, - required this.autoplay, - required this.loop, - required this.muted, - required this.width, - required this.height, - required dom.Element node, - }) : super(name: name, style: Style(), node: node, elementId: node.id); - - @override - Widget toWidget(RenderContext context) { - final double _width = width ?? (height ?? 150) * 2; - final double _height = height ?? (width ?? 300) / 2; - return AspectRatio( - aspectRatio: _width / _height, - child: Container( - key: AnchorKey.of(context.parser.key, this), - child: Chewie( - controller: ChewieController( - videoPlayerController: VideoPlayerController.network( - src.first ?? "", - ), - placeholder: poster != null - ? Image.network(poster!) - : Container(color: Colors.black), - autoPlay: autoplay, - looping: loop, - showControls: showControls, - autoInitialize: true, - aspectRatio: _width / _height, - ), - ), - ), - ); - } -} - -/// [SvgContentElement] is a [ReplacedElement] with an SVG as its contents. -class SvgContentElement extends ReplacedElement { - final String data; - final double? width; - final double? height; - - SvgContentElement({ - required String name, - required this.data, - required this.width, - required this.height, - required dom.Element node, - }) : super(name: name, style: Style(), node: node, elementId: node.id); - - @override - Widget toWidget(RenderContext context) { - return SvgPicture.string( - data, - key: AnchorKey.of(context.parser.key, this), - width: width, - height: height, - ); - } -} - class EmptyContentElement extends ReplacedElement { EmptyContentElement({String name = "empty"}) : super(name: name, style: Style(), elementId: "[[No ID]]"); @@ -275,109 +149,10 @@ class RubyElement extends ReplacedElement { } } -class MathElement extends ReplacedElement { - dom.Element element; - String? texStr; - - MathElement({ - required this.element, - this.texStr, - String name = "math", - }) : super(name: name, alignment: PlaceholderAlignment.middle, style: Style(), elementId: element.id); - - @override - Widget toWidget(RenderContext context) { - texStr = parseMathRecursive(element, r''); - return Container( - width: MediaQuery.of(context.buildContext).size.width, - child: Math.tex( - texStr ?? '', - mathStyle: MathStyle.display, - textStyle: context.style.generateTextStyle(), - onErrorFallback: (FlutterMathException e) { - if (context.parser.onMathError != null) { - return context.parser.onMathError!.call(texStr ?? '', e.message, e.messageWithType); - } else { - return Text(e.message); - } - }, - ) - ); - } - - String parseMathRecursive(dom.Node node, String parsed) { - if (node is dom.Element) { - List nodeList = node.nodes.whereType().toList(); - if (node.localName == "math" || node.localName == "mrow") { - nodeList.forEach((element) { - parsed = parseMathRecursive(element, parsed); - }); - } - // note: munder, mover, and munderover do not support placing braces and other - // markings above/below elements, instead they are treated as super/subscripts for now. - if ((node.localName == "msup" || node.localName == "msub" - || node.localName == "munder" || node.localName == "mover") && nodeList.length == 2) { - parsed = parseMathRecursive(nodeList[0], parsed); - parsed = parseMathRecursive(nodeList[1], - parsed + "${node.localName == "msup" || node.localName == "mover" ? "^" : "_"}{") + "}"; - } - if ((node.localName == "msubsup" || node.localName == "munderover") && nodeList.length == 3) { - parsed = parseMathRecursive(nodeList[0], parsed); - parsed = parseMathRecursive(nodeList[1], parsed + "_{") + "}"; - parsed = parseMathRecursive(nodeList[2], parsed + "^{") + "}"; - } - if (node.localName == "mfrac" && nodeList.length == 2) { - parsed = parseMathRecursive(nodeList[0], parsed + r"\frac{") + "}"; - parsed = parseMathRecursive(nodeList[1], parsed + "{") + "}"; - } - // note: doesn't support answer & intermediate steps - if (node.localName == "mlongdiv" && nodeList.length == 4) { - parsed = parseMathRecursive(nodeList[0], parsed); - parsed = parseMathRecursive(nodeList[2], parsed + r"\overline{)") + "}"; - } - if (node.localName == "msqrt" && nodeList.length == 1) { - parsed = parseMathRecursive(nodeList[0], parsed + r"\sqrt{") + "}"; - } - if (node.localName == "mroot" && nodeList.length == 2) { - parsed = parseMathRecursive(nodeList[1], parsed + r"\sqrt[") + "]"; - parsed = parseMathRecursive(nodeList[0], parsed + "{") + "}"; - } - if (node.localName == "mi" || node.localName == "mn" || node.localName == "mo") { - if (mathML2Tex.keys.contains(node.text.trim())) { - parsed = parsed + mathML2Tex[mathML2Tex.keys.firstWhere((e) => e == node.text.trim())]!; - } else if (node.text.startsWith("&") && node.text.endsWith(";")) { - parsed = parsed + node.text.trim().replaceFirst("&", r"\").substring(0, node.text.trim().length - 1); - } else { - parsed = parsed + node.text.trim(); - } - } - } - return parsed; - } -} - ReplacedElement parseReplacedElement( dom.Element element, - NavigationDelegate? navigationDelegateForIframe, ) { switch (element.localName) { - case "audio": - final sources = [ - if (element.attributes['src'] != null) element.attributes['src'], - ...ReplacedElement.parseMediaSources(element.children), - ]; - if (sources.isEmpty || sources.first == null) { - return EmptyContentElement(); - } - return AudioContentElement( - name: "audio", - src: sources, - showControls: element.attributes['controls'] != null, - loop: element.attributes['loop'] != null, - autoplay: element.attributes['autoplay'] != null, - muted: element.attributes['muted'] != null, - node: element, - ); case "br": return TextContentElement( text: "\n", @@ -385,15 +160,6 @@ ReplacedElement parseReplacedElement( element: element, node: element ); - case "iframe": - return IframeContentElement( - name: "iframe", - src: element.attributes['src'], - width: double.tryParse(element.attributes['width'] ?? ""), - height: double.tryParse(element.attributes['height'] ?? ""), - navigationDelegate: navigationDelegateForIframe, - node: element, - ); case "img": return ImageContentElement( name: "img", @@ -401,42 +167,10 @@ ReplacedElement parseReplacedElement( alt: element.attributes['alt'], node: element, ); - case "video": - final sources = [ - if (element.attributes['src'] != null) element.attributes['src'], - ...ReplacedElement.parseMediaSources(element.children), - ]; - if (sources.isEmpty || sources.first == null) { - return EmptyContentElement(); - } - return VideoContentElement( - name: "video", - src: sources, - poster: element.attributes['poster'], - showControls: element.attributes['controls'] != null, - loop: element.attributes['loop'] != null, - autoplay: element.attributes['autoplay'] != null, - muted: element.attributes['muted'] != null, - width: double.tryParse(element.attributes['width'] ?? ""), - height: double.tryParse(element.attributes['height'] ?? ""), - node: element, - ); - case "svg": - return SvgContentElement( - name: "svg", - data: element.outerHtml, - width: double.tryParse(element.attributes['width'] ?? ""), - height: double.tryParse(element.attributes['height'] ?? ""), - node: element, - ); case "ruby": return RubyElement( element: element, ); - case "math": - return MathElement( - element: element, - ); default: return EmptyContentElement(name: element.localName == null ? "[[No Name]]" : element.localName!); } diff --git a/lib/src/utils.dart b/lib/src/utils.dart index e37f41b93a..23b3dbda9d 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -1,6 +1,3 @@ -import 'dart:convert'; -import 'dart:math'; - import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; @@ -23,23 +20,6 @@ Map namedColors = { "Purple": "#800080", }; -Map mathML2Tex = { - "sin": r"\sin", - "sinh": r"\sinh", - "csc": r"\csc", - "csch": r"csch", - "cos": r"\cos", - "cosh": r"\cosh", - "sec": r"\sec", - "sech": r"\sech", - "tan": r"\tan", - "tanh": r"\tanh", - "cot": r"\cot", - "coth": r"\coth", - "log": r"\log", - "ln": r"\ln", -}; - class Context { T data; @@ -65,10 +45,4 @@ class CustomBorderSide { Color? color; double width; BorderStyle style; -} - -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 deleted file mode 100644 index 55223b3478..0000000000 --- a/lib/src/widgets/iframe_mobile.dart +++ /dev/null @@ -1,46 +0,0 @@ -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({ - required String name, - required this.src, - required this.width, - required this.height, - required dom.Element node, - required this.navigationDelegate, - }) : super(name: name, style: Style(), node: node, elementId: node.id); - - @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 deleted file mode 100644 index 4adae1a5d2..0000000000 --- a/lib/src/widgets/iframe_unsupported.dart +++ /dev/null @@ -1,33 +0,0 @@ -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({ - required String name, - required this.src, - required this.width, - required this.height, - required dom.Element node, - required this.navigationDelegate, - }) : super(name: name, style: Style(), node: node, elementId: node.id); - - @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 deleted file mode 100644 index 2ca3c79148..0000000000 --- a/lib/src/widgets/iframe_web.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_html/html_parser.dart'; -import 'package:flutter_html/shims/dart_ui.dart' as ui; -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; - -/// [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({ - required String name, - required this.src, - required this.width, - required this.height, - required dom.Element node, - required this.navigationDelegate, - }) : super(name: name, style: Style(), node: node, elementId: node.id); - - @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 diff --git a/packages/flutter_html_all/.gitignore b/packages/flutter_html_all/.gitignore new file mode 100644 index 0000000000..a247422ef7 --- /dev/null +++ b/packages/flutter_html_all/.gitignore @@ -0,0 +1,75 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +build/ + +# Android related +**/android/**/gradle-wrapper.jar +**/android/.gradle +**/android/captures/ +**/android/gradlew +**/android/gradlew.bat +**/android/local.properties +**/android/**/GeneratedPluginRegistrant.java + +# iOS/XCode related +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Flutter.podspec +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/ephemeral +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/Flutter/flutter_export_environment.sh +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!**/ios/**/default.mode1v3 +!**/ios/**/default.mode2v3 +!**/ios/**/default.pbxuser +!**/ios/**/default.perspectivev3 diff --git a/packages/flutter_html_all/.metadata b/packages/flutter_html_all/.metadata new file mode 100644 index 0000000000..a1f847eadf --- /dev/null +++ b/packages/flutter_html_all/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 0941968447ea8058e56e1479f7e53147149b739e + channel: beta + +project_type: package diff --git a/packages/flutter_html_all/CHANGELOG.md b/packages/flutter_html_all/CHANGELOG.md new file mode 100644 index 0000000000..41cc7d8192 --- /dev/null +++ b/packages/flutter_html_all/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/packages/flutter_html_all/LICENSE b/packages/flutter_html_all/LICENSE new file mode 100644 index 0000000000..ba75c69f7f --- /dev/null +++ b/packages/flutter_html_all/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/packages/flutter_html_all/README.md b/packages/flutter_html_all/README.md new file mode 100644 index 0000000000..dd4cc731f8 --- /dev/null +++ b/packages/flutter_html_all/README.md @@ -0,0 +1,14 @@ +# flutter_html_all + +All optional flutter_html widgets, bundled into a single package. + +## Getting Started + +This project is a starting point for a Dart +[package](https://flutter.dev/developing-packages/), +a library module containing code that can be shared easily across +multiple Flutter or Dart projects. + +For help getting started with Flutter, view our +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/packages/flutter_html_all/lib/flutter_html_all.dart b/packages/flutter_html_all/lib/flutter_html_all.dart new file mode 100644 index 0000000000..9fb8378795 --- /dev/null +++ b/packages/flutter_html_all/lib/flutter_html_all.dart @@ -0,0 +1,8 @@ +library flutter_html_all; + +export 'package:flutter_html_audio/flutter_html_audio.dart'; +export 'package:flutter_html_iframe/flutter_html_iframe.dart'; +export 'package:flutter_html_math/flutter_html_math.dart'; +export 'package:flutter_html_svg/flutter_html_svg.dart'; +export 'package:flutter_html_table/flutter_html_table.dart'; +export 'package:flutter_html_video/flutter_html_video.dart'; diff --git a/packages/flutter_html_all/pubspec.yaml b/packages/flutter_html_all/pubspec.yaml new file mode 100644 index 0000000000..f6d09e79b6 --- /dev/null +++ b/packages/flutter_html_all/pubspec.yaml @@ -0,0 +1,66 @@ +name: flutter_html_all +description: All optional flutter_html widgets, bundled into a single package. +version: 0.0.1 +homepage: + +environment: + sdk: ">=2.12.0 <3.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + # todo change all these + flutter_html_audio: + path: ../flutter_html_audio + flutter_html_iframe: + path: ../flutter_html_iframe + flutter_html_math: + path: ../flutter_html_math + flutter_html_svg: + path: ../flutter_html_svg + flutter_html_table: + path: ../flutter_html_table + flutter_html_video: + path: ../flutter_html_video + +dev_dependencies: + flutter_test: + sdk: flutter + +# 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. +flutter: + + # To add assets to your 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 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/packages/flutter_html_audio/.gitignore b/packages/flutter_html_audio/.gitignore new file mode 100644 index 0000000000..a247422ef7 --- /dev/null +++ b/packages/flutter_html_audio/.gitignore @@ -0,0 +1,75 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +build/ + +# Android related +**/android/**/gradle-wrapper.jar +**/android/.gradle +**/android/captures/ +**/android/gradlew +**/android/gradlew.bat +**/android/local.properties +**/android/**/GeneratedPluginRegistrant.java + +# iOS/XCode related +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Flutter.podspec +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/ephemeral +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/Flutter/flutter_export_environment.sh +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!**/ios/**/default.mode1v3 +!**/ios/**/default.mode2v3 +!**/ios/**/default.pbxuser +!**/ios/**/default.perspectivev3 diff --git a/packages/flutter_html_audio/.metadata b/packages/flutter_html_audio/.metadata new file mode 100644 index 0000000000..a1f847eadf --- /dev/null +++ b/packages/flutter_html_audio/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 0941968447ea8058e56e1479f7e53147149b739e + channel: beta + +project_type: package diff --git a/packages/flutter_html_audio/CHANGELOG.md b/packages/flutter_html_audio/CHANGELOG.md new file mode 100644 index 0000000000..41cc7d8192 --- /dev/null +++ b/packages/flutter_html_audio/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/packages/flutter_html_audio/LICENSE b/packages/flutter_html_audio/LICENSE new file mode 100644 index 0000000000..ba75c69f7f --- /dev/null +++ b/packages/flutter_html_audio/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/packages/flutter_html_audio/README.md b/packages/flutter_html_audio/README.md new file mode 100644 index 0000000000..3f34468d9e --- /dev/null +++ b/packages/flutter_html_audio/README.md @@ -0,0 +1,14 @@ +# flutter_html_audio + +Audio widget for flutter_html. + +## Getting Started + +This project is a starting point for a Dart +[package](https://flutter.dev/developing-packages/), +a library module containing code that can be shared easily across +multiple Flutter or Dart projects. + +For help getting started with Flutter, view our +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/packages/flutter_html_audio/lib/flutter_html_audio.dart b/packages/flutter_html_audio/lib/flutter_html_audio.dart new file mode 100644 index 0000000000..7b7d5e7e6c --- /dev/null +++ b/packages/flutter_html_audio/lib/flutter_html_audio.dart @@ -0,0 +1,37 @@ +library flutter_html_audio; + +import 'package:chewie_audio/chewie_audio.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_html/flutter_html.dart'; +import 'package:video_player/video_player.dart'; + +CustomRender audioRender = CustomRender.fromWidget(widget: (context, buildChildren) { + final sources = [ + if (context.tree.element?.attributes['src'] != null) context.tree.element!.attributes['src'], + ...ReplacedElement.parseMediaSources(context.tree.element!.children), + ]; + if (sources.isEmpty || sources.first == null) { + return Container(height: 0, width: 0); + } + return Container( + key: context.key, + width: context.style.width ?? 300, + height: Theme.of(context.buildContext).platform == TargetPlatform.android + ? 48 : 75, + child: ChewieAudio( + controller: ChewieAudioController( + videoPlayerController: VideoPlayerController.network( + sources.first ?? "", + ), + autoPlay: context.tree.element?.attributes['autoplay'] != null, + looping: context.tree.element?.attributes['loop'] != null, + showControls: context.tree.element?.attributes['controls'] != null, + autoInitialize: true, + ), + ), + ); +}); + +CustomRenderMatcher audioMatcher() => (context) { + return context.tree.element?.localName == "audio"; +}; \ No newline at end of file diff --git a/packages/flutter_html_audio/pubspec.yaml b/packages/flutter_html_audio/pubspec.yaml new file mode 100644 index 0000000000..ab65941348 --- /dev/null +++ b/packages/flutter_html_audio/pubspec.yaml @@ -0,0 +1,58 @@ +name: flutter_html_audio +description: Audio widget for flutter_html. +version: 0.0.1 +homepage: + +environment: + sdk: ">=2.12.0 <3.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + chewie_audio: '>=1.2.0 <2.0.0' + video_player: '>=2.1.1 <3.0.0' + #todo change this + flutter_html: + path: ../.. + +dev_dependencies: + flutter_test: + sdk: flutter + +# 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. +flutter: + + # To add assets to your 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 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/packages/flutter_html_iframe/.gitignore b/packages/flutter_html_iframe/.gitignore new file mode 100644 index 0000000000..a247422ef7 --- /dev/null +++ b/packages/flutter_html_iframe/.gitignore @@ -0,0 +1,75 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +build/ + +# Android related +**/android/**/gradle-wrapper.jar +**/android/.gradle +**/android/captures/ +**/android/gradlew +**/android/gradlew.bat +**/android/local.properties +**/android/**/GeneratedPluginRegistrant.java + +# iOS/XCode related +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Flutter.podspec +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/ephemeral +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/Flutter/flutter_export_environment.sh +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!**/ios/**/default.mode1v3 +!**/ios/**/default.mode2v3 +!**/ios/**/default.pbxuser +!**/ios/**/default.perspectivev3 diff --git a/packages/flutter_html_iframe/.metadata b/packages/flutter_html_iframe/.metadata new file mode 100644 index 0000000000..a1f847eadf --- /dev/null +++ b/packages/flutter_html_iframe/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 0941968447ea8058e56e1479f7e53147149b739e + channel: beta + +project_type: package diff --git a/packages/flutter_html_iframe/CHANGELOG.md b/packages/flutter_html_iframe/CHANGELOG.md new file mode 100644 index 0000000000..41cc7d8192 --- /dev/null +++ b/packages/flutter_html_iframe/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/packages/flutter_html_iframe/LICENSE b/packages/flutter_html_iframe/LICENSE new file mode 100644 index 0000000000..ba75c69f7f --- /dev/null +++ b/packages/flutter_html_iframe/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/packages/flutter_html_iframe/README.md b/packages/flutter_html_iframe/README.md new file mode 100644 index 0000000000..ea0bb66282 --- /dev/null +++ b/packages/flutter_html_iframe/README.md @@ -0,0 +1,14 @@ +# flutter_html_iframe + +Iframe widget for flutter_html. + +## Getting Started + +This project is a starting point for a Dart +[package](https://flutter.dev/developing-packages/), +a library module containing code that can be shared easily across +multiple Flutter or Dart projects. + +For help getting started with Flutter, view our +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/packages/flutter_html_iframe/lib/flutter_html_iframe.dart b/packages/flutter_html_iframe/lib/flutter_html_iframe.dart new file mode 100644 index 0000000000..ea61f8bb1e --- /dev/null +++ b/packages/flutter_html_iframe/lib/flutter_html_iframe.dart @@ -0,0 +1,11 @@ +library flutter_html_iframe; + +import 'package:flutter_html/custom_render.dart'; + +export 'iframe_unsupported.dart' + if (dart.library.io) 'iframe_mobile.dart' + if (dart.library.html) 'iframe_web.dart'; + +CustomRenderMatcher iframeMatcher() => (context) { + return context.tree.element?.localName == "iframe"; +}; \ No newline at end of file diff --git a/packages/flutter_html_iframe/lib/iframe_mobile.dart b/packages/flutter_html_iframe/lib/iframe_mobile.dart new file mode 100644 index 0000000000..3dbb19e476 --- /dev/null +++ b/packages/flutter_html_iframe/lib/iframe_mobile.dart @@ -0,0 +1,27 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_html/flutter_html.dart'; +import 'package:webview_flutter/webview_flutter.dart'; + +CustomRender iframeRender({NavigationDelegate? navigationDelegate}) => CustomRender.fromWidget(widget: (context, buildChildren) { + final sandboxMode = context.tree.element?.attributes["sandbox"]; + final UniqueKey key = UniqueKey(); + return Container( + width: double.tryParse(context.tree.element?.attributes['width'] ?? "") + ?? (double.tryParse(context.tree.element?.attributes['height'] ?? "") ?? 150) * 2, + height: double.tryParse(context.tree.element?.attributes['height'] ?? "") + ?? (double.tryParse(context.tree.element?.attributes['width'] ?? "") ?? 300) / 2, + child: WebView( + initialUrl: context.tree.element?.attributes['src'], + key: key, + javascriptMode: sandboxMode == null || sandboxMode == "allow-scripts" + ? JavascriptMode.unrestricted + : JavascriptMode.disabled, + navigationDelegate: navigationDelegate, + gestureRecognizers: { + Factory(() => VerticalDragGestureRecognizer()) + }, + ), + ); +}); diff --git a/packages/flutter_html_iframe/lib/iframe_unsupported.dart b/packages/flutter_html_iframe/lib/iframe_unsupported.dart new file mode 100644 index 0000000000..4cf0d4ed13 --- /dev/null +++ b/packages/flutter_html_iframe/lib/iframe_unsupported.dart @@ -0,0 +1,9 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_html/flutter_html.dart'; +import 'package:webview_flutter/webview_flutter.dart'; + +CustomRender iframeRender({NavigationDelegate? navigationDelegate}) => CustomRender.fromWidget(widget: (context, buildChildren) { + return Container( + child: Text("Iframes are currently not supported in this environment"), + ); +}); \ No newline at end of file diff --git a/packages/flutter_html_iframe/lib/iframe_web.dart b/packages/flutter_html_iframe/lib/iframe_web.dart new file mode 100644 index 0000000000..5f4b062672 --- /dev/null +++ b/packages/flutter_html_iframe/lib/iframe_web.dart @@ -0,0 +1,40 @@ +import 'dart:convert'; +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:flutter_html/flutter_html.dart'; +import 'package:flutter_html/shims/dart_ui.dart' as ui; +// ignore: avoid_web_libraries_in_flutter +import 'dart:html' as html; + +import 'package:webview_flutter/webview_flutter.dart'; + +CustomRender iframeRender({NavigationDelegate? navigationDelegate}) => CustomRender.fromWidget(widget: (context, buildChildren) { + final html.IFrameElement iframe = html.IFrameElement() + ..width = (double.tryParse(context.tree.element?.attributes['width'] ?? "") + ?? (double.tryParse(context.tree.element?.attributes['height'] ?? "") ?? 150) * 2).toString() + ..height = (double.tryParse(context.tree.element?.attributes['height'] ?? "") + ?? (double.tryParse(context.tree.element?.attributes['width'] ?? "") ?? 300) / 2).toString() + ..src = context.tree.element?.attributes['src'] + ..style.border = 'none'; + final String createdViewId = getRandString(10); + ui.platformViewRegistry.registerViewFactory(createdViewId, (int viewId) => iframe); + return Container( + width: double.tryParse(context.tree.element?.attributes['width'] ?? "") + ?? (double.tryParse(context.tree.element?.attributes['height'] ?? "") ?? 150) * 2, + height: double.tryParse(context.tree.element?.attributes['height'] ?? "") + ?? (double.tryParse(context.tree.element?.attributes['width'] ?? "") ?? 300) / 2, + child: Directionality( + textDirection: TextDirection.ltr, + child: HtmlElementView( + viewType: createdViewId, + ) + ) + ); +}); + +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/packages/flutter_html_iframe/pubspec.yaml b/packages/flutter_html_iframe/pubspec.yaml new file mode 100644 index 0000000000..fd7dd469bd --- /dev/null +++ b/packages/flutter_html_iframe/pubspec.yaml @@ -0,0 +1,57 @@ +name: flutter_html_iframe +description: Iframe widget for flutter_html. +version: 0.0.1 +homepage: + +environment: + sdk: ">=2.12.0 <3.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + webview_flutter: '>=2.0.4 <3.0.0' + # todo change this + flutter_html: + path: ../.. + +dev_dependencies: + flutter_test: + sdk: flutter + +# 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. +flutter: + + # To add assets to your 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 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/packages/flutter_html_math/.gitignore b/packages/flutter_html_math/.gitignore new file mode 100644 index 0000000000..a247422ef7 --- /dev/null +++ b/packages/flutter_html_math/.gitignore @@ -0,0 +1,75 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +build/ + +# Android related +**/android/**/gradle-wrapper.jar +**/android/.gradle +**/android/captures/ +**/android/gradlew +**/android/gradlew.bat +**/android/local.properties +**/android/**/GeneratedPluginRegistrant.java + +# iOS/XCode related +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Flutter.podspec +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/ephemeral +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/Flutter/flutter_export_environment.sh +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!**/ios/**/default.mode1v3 +!**/ios/**/default.mode2v3 +!**/ios/**/default.pbxuser +!**/ios/**/default.perspectivev3 diff --git a/packages/flutter_html_math/.metadata b/packages/flutter_html_math/.metadata new file mode 100644 index 0000000000..a1f847eadf --- /dev/null +++ b/packages/flutter_html_math/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 0941968447ea8058e56e1479f7e53147149b739e + channel: beta + +project_type: package diff --git a/packages/flutter_html_math/CHANGELOG.md b/packages/flutter_html_math/CHANGELOG.md new file mode 100644 index 0000000000..41cc7d8192 --- /dev/null +++ b/packages/flutter_html_math/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/packages/flutter_html_math/LICENSE b/packages/flutter_html_math/LICENSE new file mode 100644 index 0000000000..ba75c69f7f --- /dev/null +++ b/packages/flutter_html_math/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/packages/flutter_html_math/README.md b/packages/flutter_html_math/README.md new file mode 100644 index 0000000000..7504ca76f9 --- /dev/null +++ b/packages/flutter_html_math/README.md @@ -0,0 +1,14 @@ +# flutter_html_math + +Math widget for flutter_html. + +## Getting Started + +This project is a starting point for a Dart +[package](https://flutter.dev/developing-packages/), +a library module containing code that can be shared easily across +multiple Flutter or Dart projects. + +For help getting started with Flutter, view our +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/packages/flutter_html_math/lib/flutter_html_math.dart b/packages/flutter_html_math/lib/flutter_html_math.dart new file mode 100644 index 0000000000..79e5eab8f2 --- /dev/null +++ b/packages/flutter_html_math/lib/flutter_html_math.dart @@ -0,0 +1,96 @@ +library flutter_html_math; + +import 'package:html/dom.dart' as dom; +import 'package:flutter/material.dart'; +import 'package:flutter_html/flutter_html.dart'; +import 'package:flutter_math_fork/flutter_math.dart'; + +CustomRender mathRender = CustomRender.fromWidget(widget: (context, buildChildren) { + String texStr = context.tree.element == null ? '' : parseMathRecursive(context.tree.element!, r''); + return Container( + width: MediaQuery.of(context.buildContext).size.width, + child: Math.tex( + texStr, + mathStyle: MathStyle.display, + textStyle: context.style.generateTextStyle(), + onErrorFallback: (FlutterMathException e) { + if (context.parser.onMathError != null) { + return context.parser.onMathError!.call(texStr, e.message, e.messageWithType); + } else { + return Text(e.message); + } + }, + ) + ); +}); + +CustomRenderMatcher mathMatcher() => (context) { + return context.tree.element?.localName == "math"; +}; + +String parseMathRecursive(dom.Node node, String parsed) { + if (node is dom.Element) { + List nodeList = node.nodes.whereType().toList(); + if (node.localName == "math" || node.localName == "mrow") { + nodeList.forEach((element) { + parsed = parseMathRecursive(element, parsed); + }); + } + // note: munder, mover, and munderover do not support placing braces and other + // markings above/below elements, instead they are treated as super/subscripts for now. + if ((node.localName == "msup" || node.localName == "msub" + || node.localName == "munder" || node.localName == "mover") && nodeList.length == 2) { + parsed = parseMathRecursive(nodeList[0], parsed); + parsed = parseMathRecursive(nodeList[1], + parsed + "${node.localName == "msup" || node.localName == "mover" ? "^" : "_"}{") + "}"; + } + if ((node.localName == "msubsup" || node.localName == "munderover") && nodeList.length == 3) { + parsed = parseMathRecursive(nodeList[0], parsed); + parsed = parseMathRecursive(nodeList[1], parsed + "_{") + "}"; + parsed = parseMathRecursive(nodeList[2], parsed + "^{") + "}"; + } + if (node.localName == "mfrac" && nodeList.length == 2) { + parsed = parseMathRecursive(nodeList[0], parsed + r"\frac{") + "}"; + parsed = parseMathRecursive(nodeList[1], parsed + "{") + "}"; + } + // note: doesn't support answer & intermediate steps + if (node.localName == "mlongdiv" && nodeList.length == 4) { + parsed = parseMathRecursive(nodeList[0], parsed); + parsed = parseMathRecursive(nodeList[2], parsed + r"\overline{)") + "}"; + } + if (node.localName == "msqrt" && nodeList.length == 1) { + parsed = parseMathRecursive(nodeList[0], parsed + r"\sqrt{") + "}"; + } + if (node.localName == "mroot" && nodeList.length == 2) { + parsed = parseMathRecursive(nodeList[1], parsed + r"\sqrt[") + "]"; + parsed = parseMathRecursive(nodeList[0], parsed + "{") + "}"; + } + if (node.localName == "mi" || node.localName == "mn" || node.localName == "mo") { + if (mathML2Tex.keys.contains(node.text.trim())) { + parsed = parsed + mathML2Tex[mathML2Tex.keys.firstWhere((e) => e == node.text.trim())]!; + } else if (node.text.startsWith("&") && node.text.endsWith(";")) { + parsed = parsed + node.text.trim().replaceFirst("&", r"\").substring(0, node.text.trim().length - 1); + } else { + parsed = parsed + node.text.trim(); + } + } + } + return parsed; +} + +Map mathML2Tex = { + "sin": r"\sin", + "sinh": r"\sinh", + "csc": r"\csc", + "csch": r"csch", + "cos": r"\cos", + "cosh": r"\cosh", + "sec": r"\sec", + "sech": r"\sech", + "tan": r"\tan", + "tanh": r"\tanh", + "cot": r"\cot", + "coth": r"\coth", + "log": r"\log", + "ln": r"\ln", +}; diff --git a/packages/flutter_html_math/pubspec.yaml b/packages/flutter_html_math/pubspec.yaml new file mode 100644 index 0000000000..1bddf61603 --- /dev/null +++ b/packages/flutter_html_math/pubspec.yaml @@ -0,0 +1,57 @@ +name: flutter_html_math +description: Math widget for flutter_html. +version: 0.0.1 +homepage: + +environment: + sdk: ">=2.12.0 <3.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + flutter_math_fork: '>=0.3.2+1 <1.0.0' + #todo change this + flutter_html: + path: ../.. + +dev_dependencies: + flutter_test: + sdk: flutter + +# 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. +flutter: + + # To add assets to your 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 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/packages/flutter_html_svg/.gitignore b/packages/flutter_html_svg/.gitignore new file mode 100644 index 0000000000..a247422ef7 --- /dev/null +++ b/packages/flutter_html_svg/.gitignore @@ -0,0 +1,75 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +build/ + +# Android related +**/android/**/gradle-wrapper.jar +**/android/.gradle +**/android/captures/ +**/android/gradlew +**/android/gradlew.bat +**/android/local.properties +**/android/**/GeneratedPluginRegistrant.java + +# iOS/XCode related +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Flutter.podspec +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/ephemeral +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/Flutter/flutter_export_environment.sh +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!**/ios/**/default.mode1v3 +!**/ios/**/default.mode2v3 +!**/ios/**/default.pbxuser +!**/ios/**/default.perspectivev3 diff --git a/packages/flutter_html_svg/.metadata b/packages/flutter_html_svg/.metadata new file mode 100644 index 0000000000..a1f847eadf --- /dev/null +++ b/packages/flutter_html_svg/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 0941968447ea8058e56e1479f7e53147149b739e + channel: beta + +project_type: package diff --git a/packages/flutter_html_svg/CHANGELOG.md b/packages/flutter_html_svg/CHANGELOG.md new file mode 100644 index 0000000000..41cc7d8192 --- /dev/null +++ b/packages/flutter_html_svg/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/packages/flutter_html_svg/LICENSE b/packages/flutter_html_svg/LICENSE new file mode 100644 index 0000000000..ba75c69f7f --- /dev/null +++ b/packages/flutter_html_svg/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/packages/flutter_html_svg/README.md b/packages/flutter_html_svg/README.md new file mode 100644 index 0000000000..9b58f089b4 --- /dev/null +++ b/packages/flutter_html_svg/README.md @@ -0,0 +1,14 @@ +# flutter_html_svg + +SVG widget for flutter_html + +## Getting Started + +This project is a starting point for a Dart +[package](https://flutter.dev/developing-packages/), +a library module containing code that can be shared easily across +multiple Flutter or Dart projects. + +For help getting started with Flutter, view our +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/packages/flutter_html_svg/lib/flutter_html_svg.dart b/packages/flutter_html_svg/lib/flutter_html_svg.dart new file mode 100644 index 0000000000..01b5e15c49 --- /dev/null +++ b/packages/flutter_html_svg/lib/flutter_html_svg.dart @@ -0,0 +1,101 @@ +library flutter_html_svg; + +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter_html/flutter_html.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +CustomRender svgTagRender = CustomRender.fromWidget(widget: (context, buildChildren) { + return SvgPicture.string( + context.tree.element?.outerHtml ?? "", + key: context.key, + width: double.tryParse(context.tree.element?.attributes['width'] ?? ""), + height: double.tryParse(context.tree.element?.attributes['width'] ?? ""), + ); +}); + +CustomRender svgDataImageRender = CustomRender.fromWidget(widget: (context, buildChildren) { + final dataUri = _dataUriFormat.firstMatch(_src(context.tree.element?.attributes.cast() ?? {})!); + final data = dataUri?.namedGroup('data'); + if (data == null) return Container(height: 0, width: 0); + if (dataUri?.namedGroup('encoding') == ';base64') { + final decodedImage = base64.decode(data.trim()); + return SvgPicture.memory( + decodedImage, + width: _width(context.tree.element?.attributes.cast() ?? {}), + height: _height(context.tree.element?.attributes.cast() ?? {}), + ); + } + return SvgPicture.string(Uri.decodeFull(data)); +}); + +CustomRender svgNetworkImageRender = CustomRender.fromWidget(widget: (context, buildChildren) { + if (context.tree.element?.attributes["src"] == null) { + return Container(height: 0, width: 0); + } + return SvgPicture.network( + context.tree.element!.attributes["src"]!, + width: _width(context.tree.element!.attributes.cast()), + height: _height(context.tree.element!.attributes.cast()), + ); +}); + +CustomRender svgAssetImageRender = CustomRender.fromWidget(widget: (context, buildChildren) { + if ( _src(context.tree.element?.attributes.cast() ?? {}) == null) { + return Container(height: 0, width: 0); + } + return SvgPicture.asset( _src(context.tree.element!.attributes.cast())!); +}); + +CustomRenderMatcher svgTagMatcher() => (context) { + return context.tree.element?.localName == "svg"; +}; + +CustomRenderMatcher svgDataUriMatcher({String? encoding = 'base64', String? mime = 'image/svg+xml'}) => (context) { + if (_src(context.tree.element?.attributes.cast() ?? {}) == null) return false; + final dataUri = _dataUriFormat.firstMatch(_src(context.tree.element?.attributes.cast() ?? {})!); + return context.tree.element?.localName == "img" && + dataUri != null && + (mime == null || dataUri.namedGroup('mime') == mime) && + (encoding == null || dataUri.namedGroup('encoding') == ';$encoding'); +}; + +CustomRenderMatcher svgNetworkSourceMatcher({ + List schemas: const ["https", "http"], + List? domains, + String? extension = "svg", +}) => (context) { + if (_src(context.tree.element?.attributes.cast() ?? {}) == null) return false; + try { + final src = Uri.parse(_src(context.tree.element?.attributes.cast() ?? {})!); + return context.tree.element?.localName == "img" && + schemas.contains(src.scheme) && + (domains == null || domains.contains(src.host)) && + (extension == null || src.path.endsWith(".$extension")); + } catch (e) { + return false; + } + }; + +CustomRenderMatcher svgAssetUriMatcher() => (context) => + context.tree.element?.localName == "img" && + _src(context.tree.element?.attributes.cast() ?? {}) != null + && _src(context.tree.element?.attributes.cast() ?? {})!.startsWith("asset:") + && _src(context.tree.element?.attributes.cast() ?? {})!.endsWith(".svg"); + +final _dataUriFormat = RegExp("^(?data):(?image\/[\\w\+\-\.]+)(?;base64)?\,(?.*)"); + +String? _src(Map attributes) { + return attributes["src"]; +} + +double? _height(Map attributes) { + final heightString = attributes["height"]; + return heightString == null ? heightString as double? : double.tryParse(heightString); +} + +double? _width(Map attributes) { + final widthString = attributes["width"]; + return widthString == null ? widthString as double? : double.tryParse(widthString); +} \ No newline at end of file diff --git a/packages/flutter_html_svg/pubspec.yaml b/packages/flutter_html_svg/pubspec.yaml new file mode 100644 index 0000000000..3e839373cd --- /dev/null +++ b/packages/flutter_html_svg/pubspec.yaml @@ -0,0 +1,57 @@ +name: flutter_html_svg +description: SVG widget for flutter_html +version: 0.0.1 +homepage: + +environment: + sdk: ">=2.12.0 <3.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + flutter_svg: '>=0.22.0 <1.0.0' + #todo change this + flutter_html: + path: ../.. + +dev_dependencies: + flutter_test: + sdk: flutter + +# 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. +flutter: + + # To add assets to your 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 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/packages/flutter_html_table/.gitignore b/packages/flutter_html_table/.gitignore new file mode 100644 index 0000000000..a247422ef7 --- /dev/null +++ b/packages/flutter_html_table/.gitignore @@ -0,0 +1,75 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +build/ + +# Android related +**/android/**/gradle-wrapper.jar +**/android/.gradle +**/android/captures/ +**/android/gradlew +**/android/gradlew.bat +**/android/local.properties +**/android/**/GeneratedPluginRegistrant.java + +# iOS/XCode related +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Flutter.podspec +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/ephemeral +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/Flutter/flutter_export_environment.sh +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!**/ios/**/default.mode1v3 +!**/ios/**/default.mode2v3 +!**/ios/**/default.pbxuser +!**/ios/**/default.perspectivev3 diff --git a/packages/flutter_html_table/.metadata b/packages/flutter_html_table/.metadata new file mode 100644 index 0000000000..a1f847eadf --- /dev/null +++ b/packages/flutter_html_table/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 0941968447ea8058e56e1479f7e53147149b739e + channel: beta + +project_type: package diff --git a/packages/flutter_html_table/CHANGELOG.md b/packages/flutter_html_table/CHANGELOG.md new file mode 100644 index 0000000000..41cc7d8192 --- /dev/null +++ b/packages/flutter_html_table/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/packages/flutter_html_table/LICENSE b/packages/flutter_html_table/LICENSE new file mode 100644 index 0000000000..ba75c69f7f --- /dev/null +++ b/packages/flutter_html_table/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/packages/flutter_html_table/README.md b/packages/flutter_html_table/README.md new file mode 100644 index 0000000000..6560c32c77 --- /dev/null +++ b/packages/flutter_html_table/README.md @@ -0,0 +1,14 @@ +# flutter_html_table + +Table widget for flutter_html. + +## Getting Started + +This project is a starting point for a Dart +[package](https://flutter.dev/developing-packages/), +a library module containing code that can be shared easily across +multiple Flutter or Dart projects. + +For help getting started with Flutter, view our +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/packages/flutter_html_table/lib/flutter_html_table.dart b/packages/flutter_html_table/lib/flutter_html_table.dart new file mode 100644 index 0000000000..79d42624c3 --- /dev/null +++ b/packages/flutter_html_table/lib/flutter_html_table.dart @@ -0,0 +1,137 @@ +library flutter_html_table; + +import 'dart:math'; + +import 'package:flutter_layout_grid/flutter_layout_grid.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_html/flutter_html.dart'; + +CustomRender tableRender = CustomRender.fromWidget(widget: (context, buildChildren) { + return Container( + key: context.key, + margin: context.style.margin, + padding: context.style.padding, + decoration: BoxDecoration( + color: context.style.backgroundColor, + border: context.style.border, + ), + width: context.style.width, + height: context.style.height, + child: LayoutBuilder(builder: (_, constraints) => _layoutCells(context, constraints)), + ); +}); + +CustomRenderMatcher tableMatcher() => (context) { + return context.tree.element?.localName == "table"; +}; + +Widget _layoutCells(RenderContext context, BoxConstraints constraints) { + final rows = []; + List columnSizes = []; + for (var child in context.tree.children) { + if (child is TableStyleElement) { + // Map tags to predetermined column track sizes + columnSizes = child.children + .where((c) => c.name == "col") + .map((c) { + final span = int.tryParse(c.attributes["span"] ?? "1") ?? 1; + final colWidth = c.attributes["width"]; + return List.generate(span, (index) { + if (colWidth != null && colWidth.endsWith("%")) { + if (!constraints.hasBoundedWidth) { + // In a horizontally unbounded container; always wrap content instead of applying flex + return IntrinsicContentTrackSize(); + } + final percentageSize = double.tryParse( + colWidth.substring(0, colWidth.length - 1)); + return percentageSize != null && !percentageSize.isNaN + ? FlexibleTrackSize(percentageSize * 0.01) + : IntrinsicContentTrackSize(); + } else if (colWidth != null) { + final fixedPxSize = double.tryParse(colWidth); + return fixedPxSize != null + ? FixedTrackSize(fixedPxSize) + : IntrinsicContentTrackSize(); + } else { + return IntrinsicContentTrackSize(); + } + }); + }) + .expand((element) => element) + .toList(growable: false); + } else if (child is TableSectionLayoutElement) { + rows.addAll(child.children.whereType()); + } else if (child is TableRowLayoutElement) { + rows.add(child); + } + } + + // All table rows have a height intrinsic to their (spanned) contents + final rowSizes = + List.generate(rows.length, (_) => IntrinsicContentTrackSize()); + + // Calculate column bounds + int columnMax = rows + .map((row) => row.children + .whereType() + .fold(0, (int value, child) => value + child.colspan)) + .fold(0, max); + + // Place the cells in the rows/columns + final cells = []; + final columnRowOffset = List.generate(columnMax + 1, (_) => 0); + int rowi = 0; + for (var row in rows) { + int columni = 0; + for (var child in row.children) { + while (columnRowOffset[columni] > 0) { + columnRowOffset[columni] = columnRowOffset[columni] - 1; + columni++; + } + if (child is TableCellElement) { + cells.add(GridPlacement( + child: Container( + width: double.infinity, + padding: child.style.padding ?? row.style.padding, + decoration: BoxDecoration( + color: child.style.backgroundColor ?? row.style.backgroundColor, + border: child.style.border ?? row.style.border, + ), + child: SizedBox.expand( + child: Container( + alignment: child.style.alignment ?? + context.style.alignment ?? + Alignment.centerLeft, + child: StyledText( + textSpan: context.parser.parseTree(context, child), + style: child.style, + renderContext: context, + ), + ), + ), + ), + columnStart: columni, + columnSpan: child.colspan, + rowStart: rowi, + rowSpan: child.rowspan, + )); + columnRowOffset[columni] = child.rowspan - 1; + columni += child.colspan; + } + } + rowi++; + } + + // Create column tracks (insofar there were no colgroups that already defined them) + List finalColumnSizes = columnSizes.take(columnMax).toList(); + finalColumnSizes += List.generate( + max(0, columnMax - finalColumnSizes.length), + (_) => IntrinsicContentTrackSize()); + + return LayoutGrid( + gridFit: GridFit.loose, + columnSizes: finalColumnSizes, + rowSizes: rowSizes, + children: cells, + ); +} diff --git a/packages/flutter_html_table/pubspec.yaml b/packages/flutter_html_table/pubspec.yaml new file mode 100644 index 0000000000..d74266306f --- /dev/null +++ b/packages/flutter_html_table/pubspec.yaml @@ -0,0 +1,57 @@ +name: flutter_html_table +description: Table widget for flutter_html. +version: 0.0.1 +homepage: + +environment: + sdk: ">=2.12.0 <3.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + flutter_layout_grid: '>=1.0.1 <2.0.0' + #todo change this + flutter_html: + path: ../.. + +dev_dependencies: + flutter_test: + sdk: flutter + +# 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. +flutter: + + # To add assets to your 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 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/packages/flutter_html_video/.gitignore b/packages/flutter_html_video/.gitignore new file mode 100644 index 0000000000..a247422ef7 --- /dev/null +++ b/packages/flutter_html_video/.gitignore @@ -0,0 +1,75 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +build/ + +# Android related +**/android/**/gradle-wrapper.jar +**/android/.gradle +**/android/captures/ +**/android/gradlew +**/android/gradlew.bat +**/android/local.properties +**/android/**/GeneratedPluginRegistrant.java + +# iOS/XCode related +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Flutter.podspec +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/ephemeral +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/Flutter/flutter_export_environment.sh +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!**/ios/**/default.mode1v3 +!**/ios/**/default.mode2v3 +!**/ios/**/default.pbxuser +!**/ios/**/default.perspectivev3 diff --git a/packages/flutter_html_video/.metadata b/packages/flutter_html_video/.metadata new file mode 100644 index 0000000000..a1f847eadf --- /dev/null +++ b/packages/flutter_html_video/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 0941968447ea8058e56e1479f7e53147149b739e + channel: beta + +project_type: package diff --git a/packages/flutter_html_video/CHANGELOG.md b/packages/flutter_html_video/CHANGELOG.md new file mode 100644 index 0000000000..41cc7d8192 --- /dev/null +++ b/packages/flutter_html_video/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/packages/flutter_html_video/LICENSE b/packages/flutter_html_video/LICENSE new file mode 100644 index 0000000000..ba75c69f7f --- /dev/null +++ b/packages/flutter_html_video/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/packages/flutter_html_video/README.md b/packages/flutter_html_video/README.md new file mode 100644 index 0000000000..398b218ec7 --- /dev/null +++ b/packages/flutter_html_video/README.md @@ -0,0 +1,14 @@ +# flutter_html_video + +Video widget for flutter_html. + +## Getting Started + +This project is a starting point for a Dart +[package](https://flutter.dev/developing-packages/), +a library module containing code that can be shared easily across +multiple Flutter or Dart projects. + +For help getting started with Flutter, view our +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/packages/flutter_html_video/lib/flutter_html_video.dart b/packages/flutter_html_video/lib/flutter_html_video.dart new file mode 100644 index 0000000000..82b571cece --- /dev/null +++ b/packages/flutter_html_video/lib/flutter_html_video.dart @@ -0,0 +1,45 @@ +library flutter_html_video; + +import 'package:chewie/chewie.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_html/flutter_html.dart'; +import 'package:video_player/video_player.dart'; + +CustomRender videoRender = CustomRender.fromWidget(widget: (context, buildChildren) { + final sources = [ + if (context.tree.element?.attributes['src'] != null) context.tree.element!.attributes['src'], + ...ReplacedElement.parseMediaSources(context.tree.element!.children), + ]; + if (sources.isEmpty || sources.first == null) { + return Container(height: 0, width: 0); + } + final width = double.tryParse(context.tree.element?.attributes['width'] ?? ""); + final height = double.tryParse(context.tree.element?.attributes['height'] ?? ""); + final double _width = width ?? (height ?? 150) * 2; + final double _height = height ?? (width ?? 300) / 2; + return AspectRatio( + aspectRatio: _width / _height, + child: Container( + key: context.key, + child: Chewie( + controller: ChewieController( + videoPlayerController: VideoPlayerController.network( + sources.first ?? "", + ), + placeholder: context.tree.element?.attributes['poster'] != null + ? Image.network(context.tree.element!.attributes['poster']!) + : Container(color: Colors.black), + autoPlay: context.tree.element?.attributes['autoplay'] != null, + looping: context.tree.element?.attributes['loop'] != null, + showControls: context.tree.element?.attributes['controls'] != null, + autoInitialize: true, + aspectRatio: _width / _height, + ), + ), + ), + ); +}); + +CustomRenderMatcher videoMatcher() => (context) { + return context.tree.element?.localName == "video"; +}; \ No newline at end of file diff --git a/packages/flutter_html_video/pubspec.yaml b/packages/flutter_html_video/pubspec.yaml new file mode 100644 index 0000000000..f243a969e0 --- /dev/null +++ b/packages/flutter_html_video/pubspec.yaml @@ -0,0 +1,58 @@ +name: flutter_html_video +description: Video widget for flutter_html. +version: 0.0.1 +homepage: + +environment: + sdk: ">=2.12.0 <3.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + video_player: '>=2.1.1 <3.0.0' + chewie: '>=1.0.0 <2.0.0' + # todo change this + flutter_html: + path: ../.. + +dev_dependencies: + flutter_test: + sdk: flutter + +# 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. +flutter: + + # To add assets to your 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 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/pubspec.yaml b/pubspec.yaml index 65c066f0fa..a9a311bd2a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,29 +11,10 @@ dependencies: # Plugin for parsing html html: '>=0.15.0 <1.0.0' - # Plugins for parsing css + # Plugin for parsing css csslib: '>=0.17.0 <1.0.0' - # Plugins for rendering the tag. - flutter_layout_grid: '>=1.0.1 <2.0.0' - - # Plugins for rendering the