Created
March 13, 2020 09:02
-
-
Save Loong-T/5da59b881ccbab0937678fa0bb2a5f4c to your computer and use it in GitHub Desktop.
A Widget to show effects of some simple html tags
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
import 'package:html/dom.dart' as dom; | |
import 'package:html/parser.dart'; | |
class HtmlSpanView extends StatefulWidget { | |
final String content; | |
const HtmlSpanView(this.content); | |
@override | |
State<StatefulWidget> createState() => _HtmlSpanState(); | |
} | |
class _HtmlSpanState extends State<HtmlSpanView> { | |
bool _didFailToParse = false; | |
dom.Node _document; | |
@override | |
void initState() { | |
super.initState(); | |
_parseContent(); | |
} | |
@override | |
void didUpdateWidget(HtmlSpanView oldWidget) { | |
_parseContent(); | |
} | |
@override | |
Widget build(BuildContext context) { | |
if (_didFailToParse) { | |
return Text(widget.content); | |
} | |
return SpanViewBuilder(_document.nodes); | |
} | |
void _parseContent() { | |
try { | |
var document = parse(widget.content); | |
setState(() { | |
_document = document.body; | |
}); | |
} on Exception catch (_) { | |
setState(() { | |
_didFailToParse = true; | |
}); | |
} | |
} | |
} | |
class SpanViewBuilder extends StatelessWidget { | |
final List<dom.Node> nodes; | |
const SpanViewBuilder(this.nodes); | |
@override | |
Widget build(BuildContext context) { | |
if (nodes.isEmpty) { | |
return SizedBox.shrink(); | |
} | |
if (nodes.length == 1) { | |
return Text.rich(nodeToSpan(nodes.single)); | |
} | |
return Text.rich( | |
TextSpan(children: nodesToSpan(nodes)), | |
); | |
} | |
} | |
@visibleForTesting | |
List<TextSpan> nodesToSpan(List<dom.Node> nodes) { | |
return nodes.map((node) => nodeToSpan(node)).toList(); | |
} | |
@visibleForTesting | |
TextSpan nodeToSpan(dom.Node node) { | |
if (node is dom.Text) { | |
return TextSpan(text: node.data); | |
} | |
if (node is dom.Element) { | |
var name = node.localName; | |
switch (name) { | |
case 'b': | |
return BoldSpan(node.nodes); | |
case 'font': | |
var color; | |
try { | |
var str = node.attributes['color']; | |
if (str == null) { | |
throw FormatException(); | |
} | |
var raw = int.parse(str, radix: 16); | |
color = Color.fromRGBO(raw >> 16, (raw >> 8) & 0xFF, raw & 0xFF, 1.0); | |
} on FormatException { | |
color = Colors.black; | |
} | |
return FontSpan(color, node.nodes); | |
default: | |
return TextSpan(children: nodesToSpan(node.nodes)); | |
} | |
} | |
return TextSpan(text: node.toString()); | |
} | |
class BoldSpan extends TextSpan { | |
BoldSpan(dom.NodeList nodes) | |
: super( | |
children: nodesToSpan(nodes), | |
style: TextStyle(fontWeight: FontWeight.bold), | |
); | |
} | |
class FontSpan extends TextSpan { | |
final Color color; | |
FontSpan(this.color, dom.NodeList nodes) | |
: super( | |
children: nodesToSpan(nodes), | |
style: TextStyle(color: color), | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment