Skip to content

Commit c5ee0bd

Browse files
committed
Create initial extension and extension context classes
1 parent 505ffa5 commit c5ee0bd

File tree

2 files changed

+131
-0
lines changed

2 files changed

+131
-0
lines changed

lib/src/extension/extension.dart

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import 'package:flutter/painting.dart';
2+
import 'package:flutter_html/flutter_html.dart';
3+
import 'package:flutter_html/src/extension/extension_context.dart';
4+
5+
/// The [Extension] class allows you to customize the behavior of flutter_html
6+
/// or add additional functionality.
7+
///
8+
/// TODO add additional documentation
9+
///
10+
abstract class Extension {
11+
/// Tells the [HtmlParser] what additional tags to add to the default
12+
/// supported tag list (the extension's user can still override this by
13+
/// setting an explicit tagList on the Html widget).
14+
///
15+
/// Extension creators should override this with any additional tags
16+
/// that should be visible to the end user.
17+
List<String> get supportedTags;
18+
19+
/// This method is called to test whether or not this extension needs to do
20+
/// any work in this context.
21+
///
22+
/// Subclasses must override this method and return true if they'd like the
23+
/// other methods to be called in a certain context.
24+
bool matches(ExtensionContext context);
25+
26+
// Converts parsed HTML to a StyledElement. Need to define default behavior, or perhaps defer this step back to the Html widget by default
27+
StyledElement lex(ExtensionContext context) {
28+
throw UnimplementedError("TODO");
29+
}
30+
31+
// Called before styles are applied to the tree. Default behavior: return tree;
32+
StyledElement beforeStyle(ExtensionContext context) {
33+
return context.styledElement!;
34+
}
35+
36+
// Called after styling, but before extra elements/whitespace has been removed, margins collapsed, list characters processed, or relative values calculated. Default behavior: return tree;
37+
StyledElement beforeProcessing(ExtensionContext context) {
38+
return context.styledElement!;
39+
}
40+
41+
//The final step in the chain. Converts the StyledElement tree, with its attached `Style` elements, into an `InlineSpan` tree that includes Widget/TextSpans that can be rendered in a RichText or Text.rich widget. Need to define default behavior, or perhaps defer this step back to the Html widget by default
42+
InlineSpan parse(ExtensionContext context) {
43+
throw UnimplementedError("TODO");
44+
}
45+
46+
//Called when the Html widget is being destroyed. This would be a very good place to dispose() any controllers or free any resources that the extension uses. Default behavior: do nothing.
47+
void onDispose() {
48+
// Subclasses may override this to clean up when the extension is being disposed.
49+
}
50+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import 'dart:collection';
2+
3+
import 'package:flutter/widgets.dart';
4+
import 'package:flutter_html/html_parser.dart';
5+
import 'package:flutter_html/src/html_elements.dart';
6+
import 'package:html/dom.dart' as html;
7+
8+
/// Provides information about the current element on the Html tree for
9+
/// an [Extension] to use.
10+
class ExtensionContext {
11+
/// The HTML node being represented as a Flutter widget.
12+
final html.Node node;
13+
14+
/// Returns the name of the Html element, or an empty string if the node is
15+
/// a text content node, comment node, or any other node without a name.
16+
String get elementName {
17+
if (node is html.Element) {
18+
return (node as html.Element).localName ?? '';
19+
}
20+
21+
return '';
22+
}
23+
24+
/// Returns the HTML within this element, or an empty string if there is none.
25+
String get innerHtml {
26+
return node.sourceSpan?.text ?? '';
27+
}
28+
29+
/// Returns the list of child Elements on this html Node, or an empty list if
30+
/// there are no children.
31+
List<html.Element> get elementChildren {
32+
return node.children;
33+
}
34+
35+
/// Returns a linked hash map representing the attributes of the node, or an
36+
/// empty map if it has no attributes.
37+
LinkedHashMap<String, String> get attributes {
38+
return LinkedHashMap.from(node.attributes.map((key, value) {
39+
// Key is either a String or html.AttributeName
40+
return MapEntry(key.toString(), value);
41+
}));
42+
}
43+
44+
/// Returns the id of the element, or an empty string if it is not present
45+
String get id {
46+
if (node is html.Element) {
47+
return (node as html.Element).id;
48+
}
49+
50+
return '';
51+
}
52+
53+
/// Returns a set of classes on the element, or an empty set if none are
54+
/// present.
55+
Set<String> get classes {
56+
if (node is html.Element) {
57+
return (node as html.Element).classes;
58+
}
59+
60+
return <String>{};
61+
}
62+
63+
/// A reference to the [HtmlParser] instance. Useful for calling callbacks
64+
/// on the [Html] widget like [onLinkTap].
65+
final HtmlParser parser;
66+
67+
/// A reference to the [StyledElement] representation of this node.
68+
/// Guaranteed to be non-null only after the lexing step
69+
final StyledElement? styledElement;
70+
71+
/// 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.
72+
final BuildContext? context;
73+
74+
/// Constructs a new [ExtensionContext] object with the given information.
75+
const ExtensionContext({
76+
required this.node,
77+
required this.parser,
78+
this.styledElement,
79+
this.context,
80+
});
81+
}

0 commit comments

Comments
 (0)