From 3bd27657e654e7489151309138c04a6cd3fe336d Mon Sep 17 00:00:00 2001 From: Eric Kok Date: Tue, 23 Feb 2021 16:11:32 +0100 Subject: [PATCH 1/6] Support inner links --- example/lib/main.dart | 14 +++++++------- lib/flutter_html.dart | 9 ++++----- lib/html_parser.dart | 32 ++++++++++++++++++++++++++++---- lib/src/anchor.dart | 34 ++++++++++++++++++++++++++++++++++ lib/src/layout_element.dart | 3 +++ lib/src/replaced_element.dart | 7 +++++++ 6 files changed, 83 insertions(+), 16 deletions(-) create mode 100644 lib/src/anchor.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index 5d8d5b75c5..2247c8d31c 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -28,6 +28,7 @@ class MyHomePage extends StatefulWidget { } const htmlData = """ +

Scroll to bottom

Header 1

Header 2

Header 3

@@ -82,7 +83,7 @@ const htmlData = """

Custom Element Support (inline: and as block):

-

SVG support:

+

SVG support:

@@ -136,6 +137,7 @@ const htmlData = """ Empty source

Broken network image

Broken image +

Scroll to top

"""; class _MyHomePageState extends State { @@ -151,8 +153,7 @@ class _MyHomePageState extends State { data: htmlData, //Optional parameters: customImageRenders: { - networkSourceMatcher(domains: ["flutter.dev"]): - (context, attributes, element) { + networkSourceMatcher(domains: ["flutter.dev"]): (context, attributes, element) { return FlutterLogo(size: 36); }, networkSourceMatcher(domains: ["mydomain.com"]): networkImageRender( @@ -162,15 +163,14 @@ class _MyHomePageState extends State { ), // On relative paths starting with /wiki, prefix with a base url (attr, _) => attr["src"] != null && attr["src"].startsWith("/wiki"): - networkImageRender( - mapUrl: (url) => "https://upload.wikimedia.org" + url), + networkImageRender(mapUrl: (url) => "https://upload.wikimedia.org" + url), // Custom placeholder image for broken links networkSourceMatcher(): networkImageRender(altWidget: (_) => FlutterLogo()), }, - onLinkTap: (url) { + onLinkTap: (url, _, __, ___) { print("Opening $url..."); }, - onImageTap: (src) { + onImageTap: (src, _, __, ___) { print(src); }, onImageError: (exception, stackTrace) { diff --git a/lib/flutter_html.dart b/lib/flutter_html.dart index faf8468d3a..237690397f 100644 --- a/lib/flutter_html.dart +++ b/lib/flutter_html.dart @@ -1,6 +1,7 @@ library flutter_html; import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; import 'package:flutter_html/html_parser.dart'; import 'package:flutter_html/image_render.dart'; import 'package:flutter_html/style.dart'; @@ -44,7 +45,7 @@ class Html extends StatelessWidget { this.blacklistedElements = const [], this.style, this.navigationDelegateForIframe, - }) : super(key: key); + }) : super(key: key ?? UniqueKey()); final String data; final OnTap onLinkTap; @@ -72,10 +73,10 @@ class Html extends StatelessWidget { @override Widget build(BuildContext context) { final double width = shrinkWrap ? null : MediaQuery.of(context).size.width; - return Container( width: width, child: HtmlParser( + key: key, htmlData: data, onLinkTap: onLinkTap, onImageTap: onImageTap, @@ -83,9 +84,7 @@ class Html extends StatelessWidget { shrinkWrap: shrinkWrap, style: style, customRender: customRender, - imageRenders: {} - ..addAll(customImageRenders) - ..addAll(defaultImageRenders), + imageRenders: {}..addAll(customImageRenders)..addAll(defaultImageRenders), blacklistedElements: blacklistedElements, navigationDelegateForIframe: navigationDelegateForIframe, ), diff --git a/lib/html_parser.dart b/lib/html_parser.dart index 546bb54e99..1c5fcc6db6 100644 --- a/lib/html_parser.dart +++ b/lib/html_parser.dart @@ -7,6 +7,7 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_html/flutter_html.dart'; import 'package:flutter_html/image_render.dart'; +import 'package:flutter_html/src/anchor.dart'; import 'package:flutter_html/src/css_parser.dart'; import 'package:flutter_html/src/html_elements.dart'; import 'package:flutter_html/src/layout_element.dart'; @@ -30,6 +31,7 @@ typedef CustomRender = dynamic Function( ); class HtmlParser extends StatelessWidget { + final Key key; final String htmlData; final OnTap onLinkTap; final OnTap onImageTap; @@ -42,7 +44,10 @@ class HtmlParser extends StatelessWidget { final List blacklistedElements; final NavigationDelegate navigationDelegateForIframe; + final OnTap _onAnchorTap; + HtmlParser({ + @required this.key, @required this.htmlData, this.onLinkTap, this.onImageTap, @@ -53,7 +58,7 @@ class HtmlParser extends StatelessWidget { this.imageRenders, this.blacklistedElements, this.navigationDelegateForIframe, - }); + }): this._onAnchorTap = _handleAnchorTap(key, onLinkTap), super(key: key); @override Widget build(BuildContext context) { @@ -260,6 +265,7 @@ class HtmlParser extends StatelessWidget { final render = customRender[tree.name].call( newContext, ContainerSpan( + key: AnchorKey.of(key, tree), newContext: newContext, style: tree.style, shrinkWrap: context.parser.shrinkWrap, @@ -277,6 +283,7 @@ class HtmlParser extends StatelessWidget { ? render : WidgetSpan( child: ContainerSpan( + key: AnchorKey.of(key, tree), newContext: newContext, style: tree.style, shrinkWrap: context.parser.shrinkWrap, @@ -290,6 +297,7 @@ class HtmlParser extends StatelessWidget { if (tree.style?.display == Display.BLOCK) { return WidgetSpan( child: ContainerSpan( + key: AnchorKey.of(key, tree), newContext: newContext, style: tree.style, shrinkWrap: context.parser.shrinkWrap, @@ -302,6 +310,7 @@ class HtmlParser extends StatelessWidget { } else if (tree.style?.display == Display.LIST_ITEM) { return WidgetSpan( child: ContainerSpan( + key: AnchorKey.of(key, tree), newContext: newContext, style: tree.style, shrinkWrap: context.parser.shrinkWrap, @@ -363,18 +372,19 @@ class HtmlParser extends StatelessWidget { : childStyle.merge(childSpan.style)), semanticsLabel: childSpan.semanticsLabel, recognizer: TapGestureRecognizer() - ..onTap = () => onLinkTap?.call(tree.href, context, tree.attributes, tree.element), + ..onTap = () => _onAnchorTap(tree.href, context, tree.attributes, tree.element), ); } else { return WidgetSpan( child: RawGestureDetector( + key: AnchorKey.of(key, tree), gestures: { MultipleTapGestureRecognizer: GestureRecognizerFactoryWithHandlers< MultipleTapGestureRecognizer>( () => MultipleTapGestureRecognizer(), (instance) { - instance..onTap = () => onLinkTap?.call(tree.href, context, tree.attributes, tree.element); + instance..onTap = () => _onAnchorTap(tree.href, context, tree.attributes, tree.element); }, ), }, @@ -437,6 +447,18 @@ class HtmlParser extends StatelessWidget { } } + static OnTap _handleAnchorTap(Key key, OnTap onLinkTap) => + (String url, RenderContext context, Map attributes, dom.Element element) { + if (url.startsWith("#")) { + final anchorContext = AnchorKey.forId(key, url.substring(1))?.currentContext; + if (anchorContext != null) { + Scrollable.ensureVisible(anchorContext); + } + return; + } + onLinkTap?.call(url, context, attributes, element); + }; + /// [processWhitespace] removes unnecessary whitespace from the StyledElement tree. /// /// The criteria for determining which whitespace is replaceable is outlined @@ -739,6 +761,7 @@ class RenderContext { /// A [ContainerSpan] can have a border, background color, height, width, padding, and margin /// and can represent either an INLINE or BLOCK-level element. class ContainerSpan extends StatelessWidget { + final AnchorKey key; final Widget child; final List children; final Style style; @@ -746,12 +769,13 @@ class ContainerSpan extends StatelessWidget { final bool shrinkWrap; ContainerSpan({ + this.key, this.child, this.children, this.style, this.newContext, this.shrinkWrap = false, - }); + }): super(key: key); @override Widget build(BuildContext _) { diff --git a/lib/src/anchor.dart b/lib/src/anchor.dart new file mode 100644 index 0000000000..0fb44df8a4 --- /dev/null +++ b/lib/src/anchor.dart @@ -0,0 +1,34 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_html/src/styled_element.dart'; + +class AnchorKey extends GlobalKey { + final Key parentKey; + final String id; + + const AnchorKey._(this.parentKey, this.id) : super.constructor(); + + static AnchorKey of(Key parentKey, StyledElement id) { + return forId(parentKey, id.elementId); + } + + static AnchorKey forId(Key parentKey, String id) { + if (id == null || id.isEmpty) { + return null; + } + return AnchorKey._(parentKey, id); + } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is AnchorKey && runtimeType == other.runtimeType && parentKey == other.parentKey && id == other.id; + + @override + int get hashCode => parentKey.hashCode ^ id.hashCode; + + @override + String toString() { + return 'AnchorKey{parentKey: $parentKey, id: #$id}'; + } +} diff --git a/lib/src/layout_element.dart b/lib/src/layout_element.dart index 0950b51c18..71a969985b 100644 --- a/lib/src/layout_element.dart +++ b/lib/src/layout_element.dart @@ -2,6 +2,7 @@ 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'; @@ -32,6 +33,7 @@ class TableLayoutElement extends LayoutElement { @override Widget toWidget(RenderContext context) { return Container( + key: AnchorKey.of(context.parser.key, this), decoration: BoxDecoration( color: style.backgroundColor, border: style.border, @@ -278,6 +280,7 @@ class DetailsContentElement extends LayoutElement { } InlineSpan firstChild = childrenList?.isNotEmpty == true ? childrenList.first : null; return ExpansionTile( + key: AnchorKey.of(context.parser.key, this), expandedAlignment: Alignment.centerLeft, title: elementList?.isNotEmpty == true && elementList?.first?.localName == "summary" ? StyledText( textSpan: TextSpan( diff --git a/lib/src/replaced_element.dart b/lib/src/replaced_element.dart index 35b5b14a24..6bdcea77c1 100644 --- a/lib/src/replaced_element.dart +++ b/lib/src/replaced_element.dart @@ -7,6 +7,7 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_html/html_parser.dart'; +import 'package:flutter_html/src/anchor.dart'; import 'package:flutter_html/src/html_elements.dart'; import 'package:flutter_html/src/utils.dart'; import 'package:flutter_html/style.dart'; @@ -84,6 +85,7 @@ class ImageContentElement extends ReplacedElement { final widget = entry.value.call(context, attributes, element); if (widget != null) { return RawGestureDetector( + key: AnchorKey.of(context.parser.key, this), child: widget, gestures: { MultipleTapGestureRecognizer: GestureRecognizerFactoryWithHandlers( @@ -122,6 +124,7 @@ class IframeContentElement extends ReplacedElement { Widget toWidget(RenderContext context) { final sandboxMode = attributes["sandbox"]; return Container( + key: AnchorKey.of(context.parser.key, this), width: width ?? (height ?? 150) * 2, height: height ?? (width ?? 300) / 2, child: WebView( @@ -161,6 +164,7 @@ class AudioContentElement extends ReplacedElement { @override Widget toWidget(RenderContext context) { return Container( + key: AnchorKey.of(context.parser.key, this), width: context.style.width ?? 300, child: ChewieAudio( controller: ChewieAudioController( @@ -209,6 +213,7 @@ class VideoContentElement extends ReplacedElement { return AspectRatio( aspectRatio: _width / _height, child: Container( + key: AnchorKey.of(context.parser.key, this), child: Chewie( controller: ChewieController( videoPlayerController: VideoPlayerController.network( @@ -245,6 +250,7 @@ class SvgContentElement extends ReplacedElement { Widget toWidget(RenderContext context) { return SvgPicture.string( data, + key: AnchorKey.of(context.parser.key, this), width: width, height: height, ); @@ -300,6 +306,7 @@ class RubyElement extends ReplacedElement { } }); return Row( + key: AnchorKey.of(context.parser.key, this), crossAxisAlignment: CrossAxisAlignment.end, textBaseline: TextBaseline.alphabetic, mainAxisSize: MainAxisSize.min, From 9ec83cb04722f5a020a35ac0fcdd12910ae3bbe2 Mon Sep 17 00:00:00 2001 From: Eric Kok Date: Thu, 1 Apr 2021 23:19:11 +0200 Subject: [PATCH 2/6] Fix exception because a default non-null id is used, but not usable --- lib/src/anchor.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/anchor.dart b/lib/src/anchor.dart index a783db4e76..5aba894186 100644 --- a/lib/src/anchor.dart +++ b/lib/src/anchor.dart @@ -13,7 +13,7 @@ class AnchorKey extends GlobalKey { } static AnchorKey? forId(Key? parentKey, String? id) { - if (parentKey == null || id == null || id.isEmpty) { + if (parentKey == null || id == null || id.isEmpty || id == "[[No ID]]") { return null; } return AnchorKey._(parentKey, id); From 1d5fdbaa5472a23f5eba444fe80e7c8acdda07b3 Mon Sep 17 00:00:00 2001 From: Eric Kok Date: Tue, 6 Apr 2021 23:11:44 +0200 Subject: [PATCH 3/6] Handle anchor taps also when no onLinkTap function is attached --- lib/html_parser.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/html_parser.dart b/lib/html_parser.dart index ba5e1bf04a..9126841ef2 100644 --- a/lib/html_parser.dart +++ b/lib/html_parser.dart @@ -62,7 +62,7 @@ class HtmlParser extends StatelessWidget { required this.imageRenders, required this.blacklistedElements, required this.navigationDelegateForIframe, - }): this._onAnchorTap = key != null && onLinkTap != null ? _handleAnchorTap(key, onLinkTap): null, super(key: key); + }): this._onAnchorTap = key != null ? _handleAnchorTap(key, onLinkTap): null, super(key: key); @override Widget build(BuildContext context) { @@ -442,7 +442,7 @@ class HtmlParser extends StatelessWidget { } } - static OnTap _handleAnchorTap(Key key, OnTap onLinkTap) => + static OnTap _handleAnchorTap(Key key, OnTap? onLinkTap) => (String? url, RenderContext context, Map attributes, dom.Element? element) { if (url?.startsWith("#") == true) { final anchorContext = AnchorKey.forId(key, url!.substring(1))?.currentContext; @@ -451,7 +451,7 @@ class HtmlParser extends StatelessWidget { } return; } - onLinkTap.call(url, context, attributes, element); + onLinkTap?.call(url, context, attributes, element); }; /// [processWhitespace] removes unnecessary whitespace from the StyledElement tree. From 9a43e15cd71bbbb3cbc931cd480208cfa2253176 Mon Sep 17 00:00:00 2001 From: Eric Kok Date: Sat, 10 Apr 2021 16:11:48 +0200 Subject: [PATCH 4/6] Use a global key for internal anchors to ensure uniqueness without causing unneeded rebuilds --- example/ios/Podfile.lock | 4 ++-- lib/flutter_html.dart | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 7503100e5b..e3543a279d 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -26,8 +26,8 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c video_player: 9cc823b1d9da7e8427ee591e8438bfbcde500e6e - wakelock: bfc7955c418d0db797614075aabbc58a39ab5107 - webview_flutter: d2b4d6c66968ad042ad94cbb791f5b72b4678a96 + wakelock: b0843b2479edbf6504d8d262c2959446f35373aa + webview_flutter: 9f491a9b5a66f2573946a389b2677987b0ff8c0b PODFILE CHECKSUM: 8e679eca47255a8ca8067c4c67aab20e64cb974d diff --git a/lib/flutter_html.dart b/lib/flutter_html.dart index e957c5fc1f..c7036d9ec4 100644 --- a/lib/flutter_html.dart +++ b/lib/flutter_html.dart @@ -49,7 +49,8 @@ class Html extends StatelessWidget { this.navigationDelegateForIframe, }) : document = null, assert (data != null), - super(key: key ?? UniqueKey()); + anchorKey = GlobalKey(), + super(key: key); Html.fromDom({ Key? key, @@ -66,7 +67,11 @@ class Html extends StatelessWidget { this.navigationDelegateForIframe, }) : data = null, assert(document != null), - super(key: key ?? UniqueKey()); + anchorKey = GlobalKey(), + super(key: key); + + /// A unique key for this Html widget to ensure uniqueness of anchors + final Key anchorKey; /// The HTML data passed to the widget as a String final String? data; @@ -119,7 +124,7 @@ class Html extends StatelessWidget { return Container( width: width, child: HtmlParser( - key: key, + key: anchorKey, htmlData: doc, onLinkTap: onLinkTap, onImageTap: onImageTap, From 8dca91bf3ff19273022270fdc52294f619833705 Mon Sep 17 00:00:00 2001 From: tanay Date: Sat, 24 Apr 2021 18:49:15 -0400 Subject: [PATCH 5/6] Improve linking support to (hopefully) all elements --- lib/html_parser.dart | 18 +++++++++++++----- lib/src/interactable_element.dart | 5 ++++- lib/src/layout_element.dart | 7 ++++--- lib/src/replaced_element.dart | 23 ++++++++++++----------- lib/src/widgets/iframe_mobile.dart | 2 +- lib/src/widgets/iframe_unsupported.dart | 2 +- lib/src/widgets/iframe_web.dart | 2 +- 7 files changed, 36 insertions(+), 23 deletions(-) diff --git a/lib/html_parser.dart b/lib/html_parser.dart index 9126841ef2..1f56d0dd78 100644 --- a/lib/html_parser.dart +++ b/lib/html_parser.dart @@ -419,6 +419,7 @@ class HtmlParser extends StatelessWidget { //Requires special layout features not available in the TextStyle API. return WidgetSpan( child: Transform.translate( + key: AnchorKey.of(key, tree), offset: Offset(0, verticalOffset), child: StyledText( textSpan: TextSpan( @@ -434,10 +435,15 @@ class HtmlParser extends StatelessWidget { ); } else { ///[tree] is an inline element. - return TextSpan( - style: newContext.style.generateTextStyle(), - children: - tree.children.map((tree) => parseTree(newContext, tree)).toList(), + return WidgetSpan( + child: StyledText( + key: AnchorKey.of(key, tree), + textSpan: TextSpan( + style: newContext.style.generateTextStyle(), + children: + tree.children.map((tree) => parseTree(newContext, tree)).toList(), + ), renderContext: context, style: newContext.style, + ) ); } } @@ -809,13 +815,15 @@ class StyledText extends StatelessWidget { final Style style; final double textScaleFactor; final RenderContext renderContext; + final AnchorKey? key; const StyledText({ required this.textSpan, required this.style, this.textScaleFactor = 1.0, required this.renderContext, - }); + this.key, + }) : super(key: key); @override Widget build(BuildContext context) { diff --git a/lib/src/interactable_element.dart b/lib/src/interactable_element.dart index b3948029b1..504f185406 100644 --- a/lib/src/interactable_element.dart +++ b/lib/src/interactable_element.dart @@ -13,7 +13,8 @@ class InteractableElement extends StyledElement { required Style style, required this.href, required dom.Node node, - }) : super(name: name, children: children, style: style, node: node as dom.Element?); + required String elementId, + }) : super(name: name, children: children, style: style, node: node as dom.Element?, elementId: elementId); } /// A [Gesture] indicates the type of interaction by a user. @@ -34,6 +35,7 @@ InteractableElement parseInteractableElement( textDecoration: TextDecoration.underline, ), node: element, + elementId: element.id ); /// will never be called, just to suppress missing return warning default: @@ -43,6 +45,7 @@ InteractableElement parseInteractableElement( node: element, href: '', style: Style(), + elementId: "[[No ID]]" ); } } \ No newline at end of file diff --git a/lib/src/layout_element.dart b/lib/src/layout_element.dart index 0d569282a1..4c12ed4c59 100644 --- a/lib/src/layout_element.dart +++ b/lib/src/layout_element.dart @@ -15,8 +15,9 @@ abstract class LayoutElement extends StyledElement { LayoutElement({ String name = "[[No Name]]", required List children, + String? elementId, dom.Element? node, - }) : super(name: name, children: children, style: Style(), node: node); + }) : super(name: name, children: children, style: Style(), node: node, elementId: elementId ?? "[[No ID]]"); Widget? toWidget(RenderContext context); } @@ -26,7 +27,7 @@ class TableLayoutElement extends LayoutElement { required String name, required List children, required dom.Element node, - }) : super(name: name, children: children, node: node); + }) : super(name: name, children: children, node: node, elementId: node.id); @override Widget toWidget(RenderContext context) { @@ -265,7 +266,7 @@ class DetailsContentElement extends LayoutElement { required List children, required dom.Element node, required this.elementList, - }) : super(name: name, node: node, children: children); + }) : super(name: name, node: node, children: children, elementId: node.id); @override Widget toWidget(RenderContext context) { diff --git a/lib/src/replaced_element.dart b/lib/src/replaced_element.dart index 7b91c054d2..0a6a6eaff9 100644 --- a/lib/src/replaced_element.dart +++ b/lib/src/replaced_element.dart @@ -28,9 +28,10 @@ abstract class ReplacedElement extends StyledElement { ReplacedElement({ required String name, required Style style, + required String elementId, dom.Element? node, - this.alignment = PlaceholderAlignment.aboveBaseline - }) : super(name: name, children: [], style: style, node: node); + this.alignment = PlaceholderAlignment.aboveBaseline, + }) : super(name: name, children: [], style: style, node: node, elementId: elementId); static List parseMediaSources(List elements) { return elements @@ -53,7 +54,7 @@ class TextContentElement extends ReplacedElement { required this.text, this.node, dom.Element? element, - }) : super(name: "[text]", style: style, node: element); + }) : super(name: "[text]", style: style, node: element, elementId: "[[No ID]]"); @override String toString() { @@ -75,7 +76,7 @@ class ImageContentElement extends ReplacedElement { required this.src, required this.alt, required dom.Element node, - }) : super(name: name, style: Style(), node: node, alignment: PlaceholderAlignment.middle); + }) : super(name: name, style: Style(), node: node, alignment: PlaceholderAlignment.middle, elementId: node.id); @override Widget toWidget(RenderContext context) { @@ -115,7 +116,7 @@ class AudioContentElement extends ReplacedElement { required this.loop, required this.muted, required dom.Element node, - }) : super(name: name, style: Style(), node: node); + }) : super(name: name, style: Style(), node: node, elementId: node.id); @override Widget toWidget(RenderContext context) { @@ -161,7 +162,7 @@ class VideoContentElement extends ReplacedElement { required this.width, required this.height, required dom.Element node, - }) : super(name: name, style: Style(), node: node); + }) : super(name: name, style: Style(), node: node, elementId: node.id); @override Widget toWidget(RenderContext context) { @@ -202,8 +203,8 @@ class SvgContentElement extends ReplacedElement { required this.data, required this.width, required this.height, - required dom.Node node, - }) : super(name: name, style: Style(), node: node as dom.Element?); + required dom.Element node, + }) : super(name: name, style: Style(), node: node, elementId: node.id); @override Widget toWidget(RenderContext context) { @@ -217,7 +218,7 @@ class SvgContentElement extends ReplacedElement { } class EmptyContentElement extends ReplacedElement { - EmptyContentElement({String name = "empty"}) : super(name: name, style: Style()); + EmptyContentElement({String name = "empty"}) : super(name: name, style: Style(), elementId: "[[No ID]]"); @override Widget? toWidget(_) => null; @@ -227,7 +228,7 @@ class RubyElement extends ReplacedElement { dom.Element element; RubyElement({required this.element, String name = "ruby"}) - : super(name: name, alignment: PlaceholderAlignment.middle, style: Style()); + : super(name: name, alignment: PlaceholderAlignment.middle, style: Style(), elementId: element.id); @override Widget toWidget(RenderContext context) { @@ -282,7 +283,7 @@ class MathElement extends ReplacedElement { required this.element, this.texStr, String name = "math", - }) : super(name: name, alignment: PlaceholderAlignment.middle, style: Style()); + }) : super(name: name, alignment: PlaceholderAlignment.middle, style: Style(), elementId: element.id); @override Widget toWidget(RenderContext context) { diff --git a/lib/src/widgets/iframe_mobile.dart b/lib/src/widgets/iframe_mobile.dart index bd9c7e02c8..55223b3478 100644 --- a/lib/src/widgets/iframe_mobile.dart +++ b/lib/src/widgets/iframe_mobile.dart @@ -22,7 +22,7 @@ class IframeContentElement extends ReplacedElement { required this.height, required dom.Element node, required this.navigationDelegate, - }) : super(name: name, style: Style(), node: node); + }) : super(name: name, style: Style(), node: node, elementId: node.id); @override Widget toWidget(RenderContext context) { diff --git a/lib/src/widgets/iframe_unsupported.dart b/lib/src/widgets/iframe_unsupported.dart index f7a74fde3e..4adae1a5d2 100644 --- a/lib/src/widgets/iframe_unsupported.dart +++ b/lib/src/widgets/iframe_unsupported.dart @@ -20,7 +20,7 @@ class IframeContentElement extends ReplacedElement { required this.height, required dom.Element node, required this.navigationDelegate, - }) : super(name: name, style: Style(), node: node); + }) : super(name: name, style: Style(), node: node, elementId: node.id); @override Widget toWidget(RenderContext context) { diff --git a/lib/src/widgets/iframe_web.dart b/lib/src/widgets/iframe_web.dart index 1a9c348461..a70de148da 100644 --- a/lib/src/widgets/iframe_web.dart +++ b/lib/src/widgets/iframe_web.dart @@ -25,7 +25,7 @@ class IframeContentElement extends ReplacedElement { required this.height, required dom.Element node, required this.navigationDelegate, - }) : super(name: name, style: Style(), node: node); + }) : super(name: name, style: Style(), node: node, elementId: node.id); @override Widget toWidget(RenderContext context) { From 4866bef99ef5c4fae8c4db515d7025b41cddfd2c Mon Sep 17 00:00:00 2001 From: Eric Kok Date: Thu, 29 Apr 2021 10:51:03 +0200 Subject: [PATCH 6/6] Revert regression when trying to add anchor support for inline elements --- lib/html_parser.dart | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/lib/html_parser.dart b/lib/html_parser.dart index fb2b5cfe1f..203fb2806d 100644 --- a/lib/html_parser.dart +++ b/lib/html_parser.dart @@ -435,15 +435,10 @@ class HtmlParser extends StatelessWidget { ); } else { ///[tree] is an inline element. - return WidgetSpan( - child: StyledText( - key: AnchorKey.of(key, tree), - textSpan: TextSpan( - style: newContext.style.generateTextStyle(), - children: - tree.children.map((tree) => parseTree(newContext, tree)).toList(), - ), renderContext: context, style: newContext.style, - ) + return TextSpan( + style: newContext.style.generateTextStyle(), + children: + tree.children.map((tree) => parseTree(newContext, tree)).toList(), ); } }