@@ -7,6 +7,7 @@ import 'package:flutter/gestures.dart';
7
7
import 'package:flutter/material.dart' ;
8
8
import 'package:flutter_html/flutter_html.dart' ;
9
9
import 'package:flutter_html/image_render.dart' ;
10
+ import 'package:flutter_html/src/anchor.dart' ;
10
11
import 'package:flutter_html/src/css_parser.dart' ;
11
12
import 'package:flutter_html/src/html_elements.dart' ;
12
13
import 'package:flutter_html/src/layout_element.dart' ;
@@ -33,6 +34,7 @@ typedef CustomRender = dynamic Function(
33
34
);
34
35
35
36
class HtmlParser extends StatelessWidget {
37
+ final Key ? key;
36
38
final dom.Document htmlData;
37
39
final OnTap ? onLinkTap;
38
40
final OnTap ? onImageTap;
@@ -45,8 +47,10 @@ class HtmlParser extends StatelessWidget {
45
47
final Map <ImageSourceMatcher , ImageRender > imageRenders;
46
48
final List <String > tagsList;
47
49
final NavigationDelegate ? navigationDelegateForIframe;
50
+ final OnTap ? _onAnchorTap;
48
51
49
52
HtmlParser ({
53
+ required this .key,
50
54
required this .htmlData,
51
55
required this .onLinkTap,
52
56
required this .onImageTap,
@@ -58,7 +62,7 @@ class HtmlParser extends StatelessWidget {
58
62
required this .imageRenders,
59
63
required this .tagsList,
60
64
required this .navigationDelegateForIframe,
61
- });
65
+ }): this ._onAnchorTap = key != null ? _handleAnchorTap (key, onLinkTap) : null , super (key : key) ;
62
66
63
67
@override
64
68
Widget build (BuildContext context) {
@@ -250,6 +254,7 @@ class HtmlParser extends StatelessWidget {
250
254
final render = customRender[tree.name]! .call (
251
255
newContext,
252
256
ContainerSpan (
257
+ key: AnchorKey .of (key, tree),
253
258
newContext: newContext,
254
259
style: tree.style,
255
260
shrinkWrap: context.parser.shrinkWrap,
@@ -262,6 +267,7 @@ class HtmlParser extends StatelessWidget {
262
267
? render
263
268
: WidgetSpan (
264
269
child: ContainerSpan (
270
+ key: AnchorKey .of (key, tree),
265
271
newContext: newContext,
266
272
style: tree.style,
267
273
shrinkWrap: context.parser.shrinkWrap,
@@ -275,6 +281,7 @@ class HtmlParser extends StatelessWidget {
275
281
if (tree.style.display == Display .BLOCK ) {
276
282
return WidgetSpan (
277
283
child: ContainerSpan (
284
+ key: AnchorKey .of (key, tree),
278
285
newContext: newContext,
279
286
style: tree.style,
280
287
shrinkWrap: context.parser.shrinkWrap,
@@ -293,6 +300,7 @@ class HtmlParser extends StatelessWidget {
293
300
294
301
return WidgetSpan (
295
302
child: ContainerSpan (
303
+ key: AnchorKey .of (key, tree),
296
304
newContext: newContext,
297
305
style: tree.style,
298
306
shrinkWrap: context.parser.shrinkWrap,
@@ -357,18 +365,23 @@ class HtmlParser extends StatelessWidget {
357
365
: childStyle.merge (childSpan.style)),
358
366
semanticsLabel: childSpan.semanticsLabel,
359
367
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 ,
361
370
);
362
371
} else {
363
372
return WidgetSpan (
364
373
child: RawGestureDetector (
374
+ key: AnchorKey .of (key, tree),
365
375
gestures: {
366
376
MultipleTapGestureRecognizer :
367
377
GestureRecognizerFactoryWithHandlers <
368
378
MultipleTapGestureRecognizer >(
369
379
() => MultipleTapGestureRecognizer (),
370
380
(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 ;
372
385
},
373
386
),
374
387
},
@@ -406,6 +419,7 @@ class HtmlParser extends StatelessWidget {
406
419
//Requires special layout features not available in the TextStyle API.
407
420
return WidgetSpan (
408
421
child: Transform .translate (
422
+ key: AnchorKey .of (key, tree),
409
423
offset: Offset (0 , verticalOffset),
410
424
child: StyledText (
411
425
textSpan: TextSpan (
@@ -424,11 +438,23 @@ class HtmlParser extends StatelessWidget {
424
438
return TextSpan (
425
439
style: newContext.style.generateTextStyle (),
426
440
children:
427
- tree.children.map ((tree) => parseTree (newContext, tree)).toList (),
441
+ tree.children.map ((tree) => parseTree (newContext, tree)).toList (),
428
442
);
429
443
}
430
444
}
431
445
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
+
432
458
/// [processWhitespace] removes unnecessary whitespace from the StyledElement tree.
433
459
///
434
460
/// The criteria for determining which whitespace is replaceable is outlined
@@ -738,19 +764,21 @@ class RenderContext {
738
764
/// A [ContainerSpan] can have a border, background color, height, width, padding, and margin
739
765
/// and can represent either an INLINE or BLOCK-level element.
740
766
class ContainerSpan extends StatelessWidget {
767
+ final AnchorKey ? key;
741
768
final Widget ? child;
742
769
final List <InlineSpan >? children;
743
770
final Style style;
744
771
final RenderContext newContext;
745
772
final bool shrinkWrap;
746
773
747
774
ContainerSpan ({
775
+ this .key,
748
776
this .child,
749
777
this .children,
750
778
required this .style,
751
779
required this .newContext,
752
780
this .shrinkWrap = false ,
753
- });
781
+ }): super (key : key) ;
754
782
755
783
@override
756
784
Widget build (BuildContext _) {
@@ -782,13 +810,15 @@ class StyledText extends StatelessWidget {
782
810
final Style style;
783
811
final double textScaleFactor;
784
812
final RenderContext renderContext;
813
+ final AnchorKey ? key;
785
814
786
815
const StyledText ({
787
816
required this .textSpan,
788
817
required this .style,
789
818
this .textScaleFactor = 1.0 ,
790
819
required this .renderContext,
791
- });
820
+ this .key,
821
+ }) : super (key: key);
792
822
793
823
@override
794
824
Widget build (BuildContext context) {
0 commit comments