parse method
Converts HTML content to a list of TextSpan
objects
Implementation
List<TextSpan> parse() {
List<TextSpan> spans = <TextSpan>[];
for (final XmlEvent event in _events) {
if (event is XmlStartElementEvent) {
if (!event.isSelfClosing) {
String styles = '';
final String tagName = event.name.toLowerCase();
TextStyle? overrideStyles;
final double? defaultFontSize = defaultTextStyle?.fontSize;
if (overrideStyleMap?.containsKey(tagName) == true) {
overrideStyles = overrideStyleMap?[tagName];
}
switch (tagName) {
case 'h1':
double h1;
if (defaultFontSize == null) {
h1 = Theme.of(context).textTheme.headline5?.fontSize ?? 24.0;
} else {
h1 = defaultFontSize * 2;
}
styles = 'font-size: ${h1}px;';
break;
case 'h2':
double h2;
if (defaultFontSize == null) {
h2 = Theme.of(context).textTheme.headline6?.fontSize ?? 20.0;
} else {
h2 = defaultFontSize * 1.5;
}
styles = 'font-size: ${h2}px; font-weight: medium;';
break;
case 'h3':
double h3;
if (defaultFontSize == null) {
h3 = Theme.of(context).textTheme.subtitle1?.fontSize ?? 16.0;
} else {
h3 = defaultFontSize * 1.17;
}
styles = 'font-size: ${h3}px;';
break;
case 'h4':
double h4;
if (defaultFontSize == null) {
h4 = Theme.of(context).textTheme.bodyText1?.fontSize ?? 16.0;
} else {
h4 = defaultFontSize;
}
styles = 'font-size: ${h4}px; font-weight: medium;';
break;
case 'h5':
double h5;
if (defaultFontSize == null) {
h5 = Theme.of(context).textTheme.bodyText1?.fontSize ?? 16.0;
} else {
h5 = defaultFontSize * .83;
}
styles = 'font-size: ${h5}px; font-weight: bold;';
break;
case 'h6':
double h6;
if (defaultFontSize == null) {
h6 = Theme.of(context).textTheme.bodyText2?.fontSize ?? 14.0;
} else {
h6 = defaultFontSize * .67;
}
styles = 'font-size: ${h6}px; font-weight: bold;';
break;
case 'b':
styles = 'font-weight: bold;';
break;
case 'strong':
styles = 'font-weight: bold;';
break;
case 'i':
styles = 'font-style: italic;';
break;
case 'em':
styles = 'font-style: italic;';
break;
case 'u':
styles = 'text-decoration: underline;';
break;
case 'strike':
styles = 'text-decoration: line-through;';
break;
case 'del':
styles = 'text-decoration: line-through;';
break;
case 's':
styles = 'text-decoration: line-through;';
break;
case 'a':
styles =
'''visit_link:__#TO_GET#__; text-decoration: underline; color: #4287f5;''';
break;
// dropping partial support for ul-li bullets
// case 'li':
// styles = 'list_item:ul;';
// break;
// RichText(
// text: TextSpan(
// text:'',
// style: TextStyle(color: Colors.black),
// children: <InlineSpan>[
// WidgetSpan(
// alignment: PlaceholderAlignment.baseline,
// baseline: TextBaseline.alphabetic,
// child: Row(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: <Widget>[
// Text( '• '),
// SizedBox(width: 20,),
// Expanded(child: Text('Example text',)),
// ],
// )
// ),
// ],
// ),
// )
}
for (final XmlEventAttribute attribute in event.attributes) {
if (attribute.name == 'style') {
styles = '$styles;${attribute.value}';
} else if (attribute.name == 'href') {
styles = styles.replaceFirst('__#TO_GET#__',
attribute.value.replaceAll(':', '__#COLON#__'));
}
}
_stack.add(_Tag(event.name, styles, overrideStyles));
} else {
if (event.name == 'br') {
spans.add(const TextSpan(text: '\n'));
}
}
}
// TODO: see if there is a better way to add space after these tags
// maybe use widget spans
if (event is XmlEndElementEvent) {
if (event.name == 'p' ||
event.name == 'h1' ||
event.name == 'h2' ||
event.name == 'h3' ||
event.name == 'h4' ||
event.name == 'h5' ||
event.name == 'h6' ||
event.name == 'div') {
spans.add(const TextSpan(text: '\n\n'));
} else if (event.name == 'li') {
spans.add(const TextSpan(text: '\n'));
} else if (event.name == 'ul' || event.name == 'ol') {
spans.add(const TextSpan(text: '\n'));
}
if (_stack.isNotEmpty) {
final _Tag top = _stack.removeLast();
if (top.name != event.name) {
debugPrint('Malformed HTML');
return const <TextSpan>[];
}
} else {
debugPrint('Malformed HTML. Starting TAG missing');
}
}
if (event is XmlTextEvent) {
final TextSpan currentSpan = _handleText(event.text);
if (currentSpan.text?.isNotEmpty == true) {
spans.add(currentSpan);
}
}
}
// removing all extra new line textSpans to avoid space at the bottom
if (spans.isNotEmpty) {
final List<TextSpan> reversed = spans.reversed.toList();
while (reversed.isNotEmpty &&
(reversed.first.text == '\n\n' || reversed.first.text == '\n')) {
reversed.removeAt(0);
}
spans = reversed.reversed.toList();
} else {
debugPrint('Empty HTML content');
}
return spans;
}