flutter_html_css_text_metrics 0.3.0
flutter_html_css_text_metrics: ^0.3.0 copied to clipboard
A flutter_html extension that applies letter-spacing, word-spacing, text-decoration-thickness and the font-family fallback list from an element's inline style.
Example #
flutter_html has no letter-spacing, word-spacing or text-decoration-thickness
support of its own. Its Style class already carries a field for each of them, and
generateTextStyle() already hands each field to the TextStyle behind every rendered
span, but the CSS declaration parser has no case for any of the three properties, so
nothing ever sets them. The same parser reads a font-family list and keeps only its
first entry. This extension connects the two ends.
import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:flutter_html_css_text_metrics/flutter_html_css_text_metrics.dart';
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) => MaterialApp(
home: Scaffold(
body: Html(
data: '''
<div style="font-size: 12px; font-weight: 500;
letter-spacing: 0.1666666667em;
text-transform: uppercase;">
What text-overline asks for, written out as inline CSS.
</div>
<p style="word-spacing: 6px;">
Six extra pixels on every space.
</p>
<p style="text-decoration: underline;
text-decoration-thickness: 3;">
An underline three times the thickness the font defines.
</p>
<p style="font-size: 40px; text-decoration: underline;
text-decoration-thickness: 10px;">
An underline about ten pixels thick. It is an approximation.
</p>
<p style="font-family: Fira Code, Menlo, monospace;">
The whole family list survives, not only Fira Code.
</p>
''',
extensions: const [CssTextMetricsHtmlExtension()],
),
),
);
}
void main() => runApp(const ExampleApp());
What it supports #
letter-spacingandword-spacing, as a length inpx,emorrem, or as a unitless zero.letter-spacing: normalandword-spacing: normal, which hand the spacing back to the font.text-decoration-thickness: autoandfrom-font, which ask for the thickness the font defines.text-decoration-thickness: <number>, which multiplies that thickness.text-decoration-thicknessas a length inpx,emorrem, or as a percentage of1em, which approximates the multiplier: the length divided by one twelfth of the element'sfont-size.10pxon a 40 px font asks for 3.0, and paints a 2.50 px line where the font's own is 1.00 px.font-family: A, B, C, which sets familyAand keepsBandCas fallbacks.!importanton any of those declarations.
Behaviour #
letter-spacing and word-spacing inherit in CSS. The extension applies them to the
element and to its descendants. A descendant that declares its own value overrides the
ancestor. A descendant that already carries a value from somewhere else keeps it: a
<code> element stays on its monospace family under an inherited font-family.
text-decoration-thickness does not inherit, and applies to the element alone. A length
and a percentage are an approximation, because Flutter takes a multiplier of the font's
own line and exposes no way to read that line's thickness. Ask for a unitless multiple
when the line has to be exact.
normal resolves to Flutter's null, not to zero. Zero would pin the tracking at
exactly zero. null lets the font apply its own, and it clears the tracking your theme
cascaded in. Material's bodyMedium carries letterSpacing: 0.25, and flutter_html
copies that onto every span.
An em length resolves against the element's own computed font-size. A rem length
resolves against the root font size, which flutter_html takes from the surrounding
DefaultTextStyle. That style carries no font size in some contexts, and the extension
then falls back to 14.
A value the extension cannot map is ignored, not guessed at. The element renders exactly as it would have without the extension. The same goes for an element whose final inline style holds none of these properties.
The extension reads the inline style="" attribute only. It does not parse <style>
blocks or stylesheets.
Pairing with a utility-class extension #
A utility-class package expands classes into inline CSS, then drops the declarations nothing downstream can render. Each one widens that set for the companion extensions it recognises.
Neither package recognises CssTextMetricsHtmlExtension:
flutter_html_bootstrap
0.1.1 and
flutter_html_vuetify
0.1.0. flutter_html_vuetify 0.1.1 does not list it either.
Vuetify's typography classes therefore still lose their letter-spacing declaration.
Registering this extension alongside one of those packages does not bring it back.
Until a release of those packages adds this one, write the property into the inline
style attribute yourself:
Html(
data: '<div style="font-size: 12px; letter-spacing: 0.1666666667em;">'
'OVERLINE</div>',
extensions: const [CssTextMetricsHtmlExtension()],
);
Registration order does not matter against a utility-class extension. That extension
expands its classes during the preStyling step, and this one reads the result during
the preProcessing step, which always runs later.
It matters against
flutter_html_css_font_size.
That extension acts at the preProcessing step too, and flutter_html runs
the extensions that match at a step in registration order. List it first:
Html(
data: '<div style="font-size: 2em">'
'<span style="font-size: 1.5em; letter-spacing: 0.1em">M</span></div>',
extensions: const [
CssFontSizeHtmlExtension(), // must come first
CssTextMetricsHtmlExtension(),
],
);
The letter spacing is 4.2 in that order and 2.1 in the other, because this extension
reads a resolved font-size only once the font size extension has written one. Where the
declaring element's parent renders at the root size, as in
<div style="font-size: 2em; letter-spacing: 0.1em">, both orders give 2.8.