Skip to content

Commit 27e33a9

Browse files
fix: improve API for ExtensionContext and export marker.dart (Sub6Resources#1273)
1 parent 15cb05e commit 27e33a9

24 files changed

+96
-84
lines changed

lib/src/builtins/details_element_builtin.dart

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,8 @@ class DetailsElementBuiltIn extends HtmlExtension {
2323
}
2424

2525
@override
26-
InlineSpan build(ExtensionContext context,
27-
Map<StyledElement, InlineSpan> Function() buildChildren) {
28-
final childList = buildChildren();
26+
InlineSpan build(ExtensionContext context) {
27+
final childList = context.builtChildrenMap!;
2928
final children = childList.values;
3029

3130
InlineSpan? firstChild = children.isNotEmpty ? children.first : null;

lib/src/builtins/image_builtin.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,7 @@ class ImageBuiltIn extends HtmlExtension {
7171
}
7272

7373
@override
74-
InlineSpan build(ExtensionContext context,
75-
Map<StyledElement, InlineSpan> Function() buildChildren) {
74+
InlineSpan build(ExtensionContext context) {
7675
final element = context.styledElement as ImageElement;
7776

7877
final imageStyle = Style(

lib/src/builtins/interactive_element_builtin.dart

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,9 @@ class InteractiveElementBuiltIn extends HtmlExtension {
3737
}
3838

3939
@override
40-
InlineSpan build(ExtensionContext context,
41-
Map<StyledElement, InlineSpan> Function() buildChildren) {
40+
InlineSpan build(ExtensionContext context) {
4241
return TextSpan(
43-
children: buildChildren().values.map((childSpan) {
42+
children: context.inlineSpanChildren!.map((childSpan) {
4443
return _processInteractableChild(context, childSpan);
4544
}).toList(),
4645
);

lib/src/builtins/ruby_builtin.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,7 @@ class RubyBuiltIn extends HtmlExtension {
3939
}
4040

4141
@override
42-
InlineSpan build(ExtensionContext context,
43-
Map<StyledElement, InlineSpan> Function() buildChildren) {
42+
InlineSpan build(ExtensionContext context) {
4443
StyledElement? node;
4544
List<Widget> widgets = <Widget>[];
4645
final rubySize = context.parser.style['rt']?.fontSize?.value ??

lib/src/builtins/styled_element_builtin.dart

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -414,8 +414,7 @@ class StyledElementBuiltIn extends HtmlExtension {
414414
}
415415

416416
@override
417-
InlineSpan build(ExtensionContext context,
418-
Map<StyledElement, InlineSpan> Function() buildChildren) {
417+
InlineSpan build(ExtensionContext context) {
419418
if (context.styledElement!.style.display == Display.listItem ||
420419
((context.styledElement!.style.display == Display.block ||
421420
context.styledElement!.style.display == Display.inlineBlock) &&
@@ -430,8 +429,7 @@ class StyledElementBuiltIn extends HtmlExtension {
430429
shrinkWrap: context.parser.shrinkWrap,
431430
childIsReplaced: ["iframe", "img", "video", "audio"]
432431
.contains(context.styledElement!.name),
433-
children: buildChildren()
434-
.entries
432+
children: context.builtChildrenMap!.entries
435433
.expandIndexed((i, child) => [
436434
child.value,
437435
if (context.parser.shrinkWrap &&
@@ -448,8 +446,7 @@ class StyledElementBuiltIn extends HtmlExtension {
448446

449447
return TextSpan(
450448
style: context.styledElement!.style.generateTextStyle(),
451-
children: buildChildren()
452-
.entries
449+
children: context.builtChildrenMap!.entries
453450
.expandIndexed((index, child) => [
454451
child.value,
455452
if (context.parser.shrinkWrap &&

lib/src/builtins/text_builtin.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,7 @@ class TextBuiltIn extends HtmlExtension {
4141
}
4242

4343
@override
44-
InlineSpan build(ExtensionContext context,
45-
Map<StyledElement, InlineSpan> Function() buildChildren) {
44+
InlineSpan build(ExtensionContext context) {
4645
if (context.styledElement is LinebreakContentElement) {
4746
return TextSpan(
4847
text: '\n',

lib/src/builtins/vertical_align_builtin.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ class VerticalAlignBuiltIn extends HtmlExtension {
2323
}
2424

2525
@override
26-
InlineSpan build(ExtensionContext context, buildChildren) {
26+
InlineSpan build(ExtensionContext context) {
2727
return WidgetSpan(
2828
child: Transform.translate(
2929
offset: Offset(0, _getVerticalOffset(context.styledElement!)),
3030
child: CssBoxWidget.withInlineSpanChildren(
31-
children: buildChildren().values.toList(),
31+
children: context.inlineSpanChildren!,
3232
style: context.styledElement!.style,
3333
),
3434
),

lib/src/extension/extension_context.dart

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'dart:collection';
22

33
import 'package:flutter/widgets.dart';
44
import 'package:flutter_html/src/html_parser.dart';
5+
import 'package:flutter_html/src/style.dart';
56
import 'package:flutter_html/src/tree/styled_element.dart';
67
import 'package:html/dom.dart' as html;
78

@@ -59,7 +60,8 @@ class ExtensionContext {
5960
}));
6061
}
6162

62-
/// Returns the id of the element, or an empty string if it is not present
63+
/// Returns the id of the element, or an empty string if it is not present or
64+
/// this Node is not an html Element.
6365
String get id {
6466
if (node is html.Element) {
6567
return (node as html.Element).id;
@@ -68,8 +70,8 @@ class ExtensionContext {
6870
return '';
6971
}
7072

71-
/// Returns a set of classes on the element, or an empty set if none are
72-
/// present.
73+
/// Returns a set of classes on this Element, or an empty set if none are
74+
/// present or this Node is not an html Element.
7375
Set<String> get classes {
7476
if (node is html.Element) {
7577
return (node as html.Element).classes;
@@ -83,38 +85,58 @@ class ExtensionContext {
8385
final HtmlParser parser;
8486

8587
/// A reference to the [StyledElement] representation of this node.
86-
/// Guaranteed to be non-null only after the lexing step
88+
/// Guaranteed to be non-null only after the preparing step
8789
final StyledElement? styledElement;
8890

89-
/// Guaranteed only when in the `parse` method of an Extension, but it might not necessarily be the nearest BuildContext. Probably should use a `Builder` Widget if you absolutely need the most relevant BuildContext.
91+
/// A reference to the [Style] on the [StyledElement] representation of this
92+
/// node. Guaranteed to be non-null only after the preparing step.
93+
Style? get style {
94+
return styledElement?.style;
95+
}
96+
97+
/// The [StyledElement] version of this node's children. Guaranteed to be
98+
/// non-null only after the preparing step.
99+
List<StyledElement> get styledElementChildren {
100+
return styledElement!.children;
101+
}
102+
103+
final BuildChildrenCallback? _callbackToBuildChildren;
104+
Map<StyledElement, InlineSpan>? _builtChildren;
105+
106+
/// A map between the original [StyledElement] children of this node and the
107+
/// fully built [InlineSpan] children of this node.
108+
Map<StyledElement, InlineSpan>? get builtChildrenMap {
109+
_builtChildren ??= _callbackToBuildChildren?.call();
110+
111+
return _builtChildren;
112+
}
113+
114+
/// The [InlineSpan] version of this node's children. Constructed lazily.
115+
/// Guaranteed to be non-null only when `currentStep` is `building`.
116+
List<InlineSpan>? get inlineSpanChildren {
117+
_builtChildren ??= _callbackToBuildChildren?.call();
118+
119+
return _builtChildren?.values.toList();
120+
}
121+
122+
/// Guaranteed to be non-null only when `currentStep` is `building`,
123+
/// but it might not necessarily be the nearest BuildContext. Probably should
124+
/// use a `Builder` Widget if you need the most relevant BuildContext.
90125
final BuildContext? buildContext;
91126

92127
/// Constructs a new [ExtensionContext] object with the given information.
93-
const ExtensionContext({
128+
ExtensionContext({
129+
required this.currentStep,
94130
required this.node,
95131
required this.parser,
96132
this.styledElement,
97133
this.buildContext,
98-
required this.currentStep,
99-
});
100-
101-
ExtensionContext copyWith({
102-
html.Node? node,
103-
HtmlParser? parser,
104-
StyledElement? styledElement,
105-
BuildContext? buildContext,
106-
CurrentStep? currentStep,
107-
}) {
108-
return ExtensionContext(
109-
node: node ?? this.node,
110-
parser: parser ?? this.parser,
111-
styledElement: styledElement ?? this.styledElement,
112-
buildContext: buildContext ?? this.buildContext,
113-
currentStep: currentStep ?? this.currentStep,
114-
);
115-
}
134+
BuildChildrenCallback? buildChildrenCallback,
135+
}) : _callbackToBuildChildren = buildChildrenCallback;
116136
}
117137

138+
typedef BuildChildrenCallback = Map<StyledElement, InlineSpan> Function();
139+
118140
enum CurrentStep {
119141
preparing,
120142
preStyling,

lib/src/extension/helpers/image_extension.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,11 @@ class ImageExtension extends ImageBuiltIn {
5959
}
6060

6161
@override
62-
InlineSpan build(ExtensionContext context, buildChildren) {
62+
InlineSpan build(ExtensionContext context) {
6363
if (builder != null) {
6464
return builder!.call(context);
6565
} else {
66-
return super.build(context, buildChildren);
66+
return super.build(context);
6767
}
6868
}
6969
}

lib/src/extension/helpers/image_tap_extension.dart

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,13 @@ class OnImageTapExtension extends ImageBuiltIn {
4444
}
4545

4646
@override
47-
InlineSpan build(ExtensionContext context, buildChildren) {
48-
final children = buildChildren();
47+
InlineSpan build(ExtensionContext context) {
48+
final children = context.builtChildrenMap!;
4949

50-
assert(children.keys.isNotEmpty,
51-
"The OnImageTapExtension has been thwarted! It no longer has an `img` child");
50+
assert(
51+
children.keys.isNotEmpty,
52+
"The OnImageTapExtension has been thwarted! It no longer has an `img` child",
53+
);
5254

5355
final actualImage = children.keys.first;
5456

lib/src/extension/helpers/matcher_extension.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class MatcherExtension extends HtmlExtension {
3838
}
3939

4040
@override
41-
InlineSpan build(ExtensionContext context, buildChildren) {
41+
InlineSpan build(ExtensionContext context) {
4242
return builder(context);
4343
}
4444
}

lib/src/extension/helpers/tag_extension.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class TagExtension extends HtmlExtension {
4545
Set<String> get supportedTags => tagsToExtend;
4646

4747
@override
48-
InlineSpan build(ExtensionContext context, buildChildren) {
48+
InlineSpan build(ExtensionContext context) {
4949
return builder(context);
5050
}
5151
}

lib/src/extension/helpers/tag_wrap_extension.dart

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,10 @@ class TagWrapExtension extends HtmlExtension {
6060
}
6161

6262
@override
63-
InlineSpan build(ExtensionContext context, buildChildren) {
64-
final children = buildChildren();
63+
InlineSpan build(ExtensionContext context) {
6564
final child = CssBoxWidget.withInlineSpanChildren(
66-
children: children.values.toList(),
67-
style: context.styledElement!.style,
65+
children: context.inlineSpanChildren!,
66+
style: context.style!,
6867
);
6968

7069
return WidgetSpan(

lib/src/extension/html_extension.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,10 @@ abstract class HtmlExtension {
5858
/// The final step in the chain. Converts the StyledElement tree, with its
5959
/// attached `Style` elements, into an `InlineSpan` tree that includes
6060
/// Widget/TextSpans that can be rendered in a RichText widget.
61-
InlineSpan build(ExtensionContext context,
62-
Map<StyledElement, InlineSpan> Function() buildChildren) {
61+
InlineSpan build(ExtensionContext context) {
6362
throw UnimplementedError(
64-
"Extension `$runtimeType` matched `${context.styledElement!.name}` but didn't implement `parse`");
63+
"Extension `$runtimeType` matched `${context.styledElement!.name}` but didn't implement `parse`",
64+
);
6565
}
6666

6767
/// Called when the Html widget is being destroyed. This would be a very

lib/src/html_parser.dart

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -125,23 +125,22 @@ class HtmlParser extends StatefulWidget {
125125
/// or HtmlExtensions available. If none of the extensions matches, returns
126126
/// an empty TextSpan.
127127
InlineSpan buildFromExtension(
128-
ExtensionContext extensionContext,
129-
Map<StyledElement, InlineSpan> Function() buildChildren, {
128+
ExtensionContext extensionContext, {
130129
Set<HtmlExtension> extensionsToIgnore = const {},
131130
}) {
132131
// Loop through every extension and see if it can handle this node
133132
for (final extension in extensions) {
134133
if (!extensionsToIgnore.contains(extension) &&
135134
extension.matches(extensionContext)) {
136-
return extension.build(extensionContext, buildChildren);
135+
return extension.build(extensionContext);
137136
}
138137
}
139138

140139
// Loop through built in elements and see if they can handle this node.
141140
for (final builtIn in builtIns) {
142141
if (!extensionsToIgnore.contains(builtIn) &&
143142
builtIn.matches(extensionContext)) {
144-
return builtIn.build(extensionContext, buildChildren);
143+
return builtIn.build(extensionContext);
145144
}
146145
}
147146

@@ -380,27 +379,28 @@ class _HtmlParserState extends State<HtmlParser> {
380379
}
381380

382381
InlineSpan _buildTreeRecursive(StyledElement tree) {
382+
// Generate a function that allows children to be built lazily
383+
Map<StyledElement, InlineSpan> buildChildren() {
384+
return Map.fromEntries(tree.children.map((child) {
385+
return MapEntry(child, _buildTreeRecursive(child));
386+
}));
387+
}
388+
383389
// Set the extension context for this node.
384390
final extensionContext = ExtensionContext(
385391
parser: widget,
386392
buildContext: context,
387393
node: tree.node,
388394
styledElement: tree,
389395
currentStep: CurrentStep.building,
396+
buildChildrenCallback: buildChildren,
390397
);
391398

392399
// Block restricted tags from getting sent to extensions
393400
if (_isTagRestricted(extensionContext)) {
394401
return const TextSpan(text: "");
395402
}
396403

397-
// Generate a function that allows children to be generated
398-
Map<StyledElement, InlineSpan> buildChildren() {
399-
return Map.fromEntries(tree.children.map((child) {
400-
return MapEntry(child, _buildTreeRecursive(child));
401-
}));
402-
}
403-
404-
return widget.buildFromExtension(extensionContext, buildChildren);
404+
return widget.buildFromExtension(extensionContext);
405405
}
406406
}

lib/src/processing/lists.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import 'dart:collection';
22

33
import 'package:collection/collection.dart';
44
import 'package:flutter_html/flutter_html.dart';
5-
import 'package:flutter_html/src/style/marker.dart';
65
import 'package:list_counter/list_counter.dart';
76

87
class ListProcessing {

lib/src/style.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ import 'dart:ui';
33
import 'package:flutter/material.dart';
44
import 'package:flutter_html/flutter_html.dart';
55
import 'package:flutter_html/src/css_parser.dart';
6-
import 'package:flutter_html/src/style/marker.dart';
76

87
//Export Style value-unit APIs
98
export 'package:flutter_html/src/style/margin.dart';
109
export 'package:flutter_html/src/style/length.dart';
1110
export 'package:flutter_html/src/style/size.dart';
1211
export 'package:flutter_html/src/style/fontsize.dart';
1312
export 'package:flutter_html/src/style/lineheight.dart';
13+
export 'package:flutter_html/src/style/marker.dart';
1414

1515
///This class represents all the available CSS attributes
1616
///for this package.

packages/flutter_html_audio/lib/flutter_html_audio.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class AudioHtmlExtension extends HtmlExtension {
1919
Set<String> get supportedTags => {"audio"};
2020

2121
@override
22-
InlineSpan build(ExtensionContext context, buildChildren) {
22+
InlineSpan build(ExtensionContext context) {
2323
return WidgetSpan(
2424
child: AudioWidget(
2525
context: context,

packages/flutter_html_iframe/lib/flutter_html_iframe.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class IframeHtmlExtension extends HtmlExtension {
1919
Set<String> get supportedTags => {"iframe"};
2020

2121
@override
22-
InlineSpan build(ExtensionContext context, buildChildren) {
22+
InlineSpan build(ExtensionContext context) {
2323
return WidgetSpan(
2424
child: IframeWidget(
2525
extensionContext: context,

0 commit comments

Comments
 (0)