@@ -7,6 +7,7 @@ import 'package:flutter/gestures.dart';
77import 'package:flutter/material.dart' ;
88import 'package:flutter_html/flutter_html.dart' ;
99import 'package:flutter_html/image_render.dart' ;
10+ import 'package:flutter_html/src/anchor.dart' ;
1011import 'package:flutter_html/src/css_parser.dart' ;
1112import 'package:flutter_html/src/html_elements.dart' ;
1213import 'package:flutter_html/src/layout_element.dart' ;
@@ -33,6 +34,7 @@ typedef CustomRender = dynamic Function(
3334);
3435
3536class HtmlParser extends StatelessWidget {
37+ final Key ? key;
3638 final dom.Document htmlData;
3739 final OnTap ? onLinkTap;
3840 final OnTap ? onImageTap;
@@ -45,8 +47,10 @@ class HtmlParser extends StatelessWidget {
4547 final Map <ImageSourceMatcher , ImageRender > imageRenders;
4648 final List <String > tagsList;
4749 final NavigationDelegate ? navigationDelegateForIframe;
50+ final OnTap ? _onAnchorTap;
4851
4952 HtmlParser ({
53+ required this .key,
5054 required this .htmlData,
5155 required this .onLinkTap,
5256 required this .onImageTap,
@@ -58,7 +62,7 @@ class HtmlParser extends StatelessWidget {
5862 required this .imageRenders,
5963 required this .tagsList,
6064 required this .navigationDelegateForIframe,
61- });
65+ }): this ._onAnchorTap = key != null ? _handleAnchorTap (key, onLinkTap) : null , super (key : key) ;
6266
6367 @override
6468 Widget build (BuildContext context) {
@@ -250,6 +254,7 @@ class HtmlParser extends StatelessWidget {
250254 final render = customRender[tree.name]! .call (
251255 newContext,
252256 ContainerSpan (
257+ key: AnchorKey .of (key, tree),
253258 newContext: newContext,
254259 style: tree.style,
255260 shrinkWrap: context.parser.shrinkWrap,
@@ -262,6 +267,7 @@ class HtmlParser extends StatelessWidget {
262267 ? render
263268 : WidgetSpan (
264269 child: ContainerSpan (
270+ key: AnchorKey .of (key, tree),
265271 newContext: newContext,
266272 style: tree.style,
267273 shrinkWrap: context.parser.shrinkWrap,
@@ -275,6 +281,7 @@ class HtmlParser extends StatelessWidget {
275281 if (tree.style.display == Display .BLOCK ) {
276282 return WidgetSpan (
277283 child: ContainerSpan (
284+ key: AnchorKey .of (key, tree),
278285 newContext: newContext,
279286 style: tree.style,
280287 shrinkWrap: context.parser.shrinkWrap,
@@ -293,6 +300,7 @@ class HtmlParser extends StatelessWidget {
293300
294301 return WidgetSpan (
295302 child: ContainerSpan (
303+ key: AnchorKey .of (key, tree),
296304 newContext: newContext,
297305 style: tree.style,
298306 shrinkWrap: context.parser.shrinkWrap,
@@ -357,18 +365,23 @@ class HtmlParser extends StatelessWidget {
357365 : childStyle.merge (childSpan.style)),
358366 semanticsLabel: childSpan.semanticsLabel,
359367 recognizer: TapGestureRecognizer ()
360- ..onTap = () => onLinkTap? .call (tree.href, context, tree.attributes, tree.element),
368+ ..onTap =
369+ _onAnchorTap != null ? () => _onAnchorTap !(tree.href, context, tree.attributes, tree.element) : null ,
361370 );
362371 } else {
363372 return WidgetSpan (
364373 child: RawGestureDetector (
374+ key: AnchorKey .of (key, tree),
365375 gestures: {
366376 MultipleTapGestureRecognizer :
367377 GestureRecognizerFactoryWithHandlers <
368378 MultipleTapGestureRecognizer >(
369379 () => MultipleTapGestureRecognizer (),
370380 (instance) {
371- instance..onTap = () => onLinkTap? .call (tree.href, context, tree.attributes, tree.element);
381+ instance
382+ ..onTap = _onAnchorTap != null
383+ ? () => _onAnchorTap !(tree.href, context, tree.attributes, tree.element)
384+ : null ;
372385 },
373386 ),
374387 },
@@ -406,6 +419,7 @@ class HtmlParser extends StatelessWidget {
406419 //Requires special layout features not available in the TextStyle API.
407420 return WidgetSpan (
408421 child: Transform .translate (
422+ key: AnchorKey .of (key, tree),
409423 offset: Offset (0 , verticalOffset),
410424 child: StyledText (
411425 textSpan: TextSpan (
@@ -424,11 +438,23 @@ class HtmlParser extends StatelessWidget {
424438 return TextSpan (
425439 style: newContext.style.generateTextStyle (),
426440 children:
427- tree.children.map ((tree) => parseTree (newContext, tree)).toList (),
441+ tree.children.map ((tree) => parseTree (newContext, tree)).toList (),
428442 );
429443 }
430444 }
431445
446+ static OnTap _handleAnchorTap (Key key, OnTap ? onLinkTap) =>
447+ (String ? url, RenderContext context, Map <String , String > attributes, dom.Element ? element) {
448+ if (url? .startsWith ("#" ) == true ) {
449+ final anchorContext = AnchorKey .forId (key, url! .substring (1 ))? .currentContext;
450+ if (anchorContext != null ) {
451+ Scrollable .ensureVisible (anchorContext);
452+ }
453+ return ;
454+ }
455+ onLinkTap? .call (url, context, attributes, element);
456+ };
457+
432458 /// [processWhitespace] removes unnecessary whitespace from the StyledElement tree.
433459 ///
434460 /// The criteria for determining which whitespace is replaceable is outlined
@@ -738,19 +764,21 @@ class RenderContext {
738764/// A [ContainerSpan] can have a border, background color, height, width, padding, and margin
739765/// and can represent either an INLINE or BLOCK-level element.
740766class ContainerSpan extends StatelessWidget {
767+ final AnchorKey ? key;
741768 final Widget ? child;
742769 final List <InlineSpan >? children;
743770 final Style style;
744771 final RenderContext newContext;
745772 final bool shrinkWrap;
746773
747774 ContainerSpan ({
775+ this .key,
748776 this .child,
749777 this .children,
750778 required this .style,
751779 required this .newContext,
752780 this .shrinkWrap = false ,
753- });
781+ }): super (key : key) ;
754782
755783 @override
756784 Widget build (BuildContext _) {
@@ -782,13 +810,15 @@ class StyledText extends StatelessWidget {
782810 final Style style;
783811 final double textScaleFactor;
784812 final RenderContext renderContext;
813+ final AnchorKey ? key;
785814
786815 const StyledText ({
787816 required this .textSpan,
788817 required this .style,
789818 this .textScaleFactor = 1.0 ,
790819 required this .renderContext,
791- });
820+ this .key,
821+ }) : super (key: key);
792822
793823 @override
794824 Widget build (BuildContext context) {
0 commit comments