leancode_markup
Simple package that allows you parse text with predefined tags, and returns styled Flutter text.
Available tags
None. Dart part of this package is agnostic to any semantics of tags.
Basic tag styles
However, we provide a list of basic tag styles for quick use. Read more in usage.
Usage
- MarkupTagStyle - define custom tag style
- DefaultMarkupTheme - apply tag styles to descendant
- MarkupText - widget for markup text
- Example
MarkupTagStyle
Define custom tag and style using MarkupTagStyle.delegate
. Pass tag name and desired style, e.g. to use tag b
to apply FontWeight.bold
to text do:
MarkupTagStyle.delegate(
tagName: 'b',
styleCreator: (_) => const TextStyle(fontWeight: FontWeight.bold),
),
MarkupTagSpanFactory
You could wrap your tagged text into any widgets. To do so, define tag factory for specified tag. Tag factory could be only defined in the tagFactories parameter in DefaultMarkupTheme
, that take as argument Map of pairs tag
:MarkupTagSpanFactory
. It's done like that, so there's a guarantee that every tag has only one factory.
MarkupTagSpanFactory
takes as parameters child Widget
, that needs to be wraped with desired widgets and optional parameter
taken from tag parsing.
It returns WidgetSpan.
DefaultMarkupTheme
DefaultMarkupTheme
is equivalent of well known DefaultTextStyle
, but for the MarkupTagStyle
. It apply markup tag styles to descendant MarkupText
widgets.
DefaultMarkupTheme
also has Map<String, MarkupTagSpanFactory> tagFactories
parameter to wrap specified tagged text with Widgets.
DefaultMarkupTheme(
logger: debugPrint,
tagStyles: [
MarkupTagStyle.delegate(
tagName: 'b',
styleCreator: (_) => const TextStyle(fontWeight: FontWeight.bold),
),
MarkupTagStyle.delegate(
tagName: 'i',
styleCreator: (_) => const TextStyle(fontStyle: FontStyle.italic),
),
],
tagFactories: {
'url': (child, parameter) {
return WidgetSpan(
child: GestureDetector(
onTap: () => launchUrl(Uri.parse(parameter!)),
child: child,
),
);
},
// More factories...
},
child: ...
),
Logging
If you want to log invalid tags, pass logging method as DefaultMarkupTheme
param.
MarkupText
Converts passed String
into Text.rich
with applied TextStyles
.
Use tag styles from ancestor DefaultMarkupTheme
const MarkupText(
'[u]underline[/u][i][b]Italic, bold text[/b][/i]',
),
Use predefined DefaultMarkupTheme.basicTag
MarkupText(
'[u]underline[/u][i][b]Italic, bold text[/b][/i]',
tagStyles: DefaultMarkupTheme.basicTags,
),
Use custom tag styles
MarkupText(
'[green][u]underline[/u][/green][i][b]Italic, bold text[/b][/i]',
tagStyles: [
MarkupTagStyle.delegate(
tagName: 'green',
styleCreator: (_) => const TextStyle(color: Colors.green),
),
],
),
Overwrite tag styles from ancestor
DefaultMarkupTheme(
tagStyles: DefaultMarkupTheme.basicTags,
child: MarkupText(
'[u]underline[/u][i][b]Italic, bold text[/b][/i]',
tagStyles: [
MarkupTagStyle.delegate(
tagName: 'b',
styleCreator: (_) => const TextStyle(fontWeight: FontWeight.w900),
),
],
),
),
Escape tag
Center(
child: MarkupText(
r'[u][i]Lorem ipsum dolor sit amet, \[b]consectetur adipiscing elit\[/b][/i][/u]',
tagStyles: DefaultMarkupTheme.basicTags,
),
),
Example
This code shows an example of usage DefaultMarkupTheme
and MarkupText
.
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// You can use `DefaultMarkupTheme` to define common tag styles for children.
DefaultMarkupTheme(
tagStyles: [
MarkupTagStyle.delegate(
tagName: 'b',
styleCreator: (_) =>
const TextStyle(fontWeight: FontWeight.bold),
),
MarkupTagStyle.delegate(
tagName: 'i',
styleCreator: (_) =>
const TextStyle(fontStyle: FontStyle.italic),
),
MarkupTagStyle.delegate(
tagName: 'u',
styleCreator: (_) =>
const TextStyle(decoration: TextDecoration.underline),
),
MarkupTagStyle.delegate(
tagName: 'url',
styleCreator: (_) => const TextStyle(color: Colors.lightBlue),
),
MarkupTagStyle.delegate(
tagName: 'yellow',
styleCreator: (_) => const TextStyle(
backgroundColor: Colors.black,
),
),
],
// Add tag factories to wrap your tagged text into any widget
tagFactories: {
// Make clickable link
'url': (child, parameter) {
return WidgetSpan(
child: GestureDetector(
onTap: () async {
if (parameter != null &&
await canLaunchUrl(Uri.parse(parameter))) {
await launchUrl(Uri.parse(parameter));
}
},
child: child,
),
);
},
// Transform text
'sup': (child, parameter) {
return WidgetSpan(
child: Transform.translate(
offset: const Offset(2, -4),
child: child,
),
);
},
},
child: Column(
children: [
// Use tag styles from `DefaultMarkupTheme` parent
const MarkupText(
'[i]Lorem ipsum dolor sit amet, [b]consectetur adipiscing elit[/b][/i]',
),
const SizedBox(height: 8),
MarkupText(
'[yellow][i]Lorem ipsum dolor sit amet, [b]consectetur adipiscing elit[/b][/i][/yellow]',
tagStyles: [
// You can add custom tag styles just for `MarkupText`.
// The rest of the tag styles will still be taken from the parent.
MarkupTagStyle.delegate(
tagName: 'yellow',
styleCreator: (_) =>
const TextStyle(color: Color(0xFFFEFF00)),
),
// You can overwrite tag styles from `DefaultMarkupTheme` parent
MarkupTagStyle.delegate(
tagName: 'b',
styleCreator: (_) =>
const TextStyle(fontWeight: FontWeight.w900),
),
],
),
const SizedBox(height: 8),
// Use tag factories to create e.g. clickable text to open link
const MarkupText(
'[url="https://leancode.co"][i]Lorem ipsum dolor sit amet, [b]consectetur adipiscing elit[/b][/i][/url]',
),
],
),
),
const SizedBox(height: 8),
// You can use `basicTags` just in `MarkupText` widget.
Center(
child: MarkupText(
'[u][i]Lorem ipsum dolor sit amet, [b]consectetur adipiscing elit[/b][/i][/u]',
tagStyles: DefaultMarkupTheme.basicTags,
),
),
const SizedBox(height: 8),
// You can escape tags using "\".
Center(
child: MarkupText(
r'[u][i]Lorem ipsum dolor sit amet, \[b]consectetur adipiscing elit\[/b][/i][/u]',
tagStyles: DefaultMarkupTheme.basicTags,
),
),
],
),
TODOs:
- Flutter tests
- Optimize rendering. Some style computations can be cached/precomputed at
DefaultMarkupTheme
level - Better error reporting