flutter_html_css_stylesheet 0.2.0
flutter_html_css_stylesheet: ^0.2.0 copied to clipboard
A flutter_html extension that resolves a CSS stylesheet and writes each element's winning declarations into its inline style, so extensions can read them.
Example #
flutter_html 3.0.0 reads a <style> block and then drops most of it. Its
styleTree() collects every block, parseExternalCss() parses it, and each matching
element takes the rule through declarationsToStyle(). That function knows 51 property
names. Everything else is discarded at parse time, with no error and no warning, before
any extension runs. This extension resolves the stylesheet itself and writes the result
into each element's inline style="", where flutter_html and every companion
extension already look.
import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:flutter_html_css_border_radius/flutter_html_css_border_radius.dart';
import 'package:flutter_html_css_stylesheet/flutter_html_css_stylesheet.dart';
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) => MaterialApp(
home: Scaffold(
body: Html(
data: '''
<style>
.card {
border-radius: 12px;
padding: 12px;
margin: 8px;
background-color: #e7f1ff;
}
.card p { color: #0d6efd; }
#note { color: #6c757d !important; }
</style>
<div class="card">
<p>Rounded, padded, and blue.</p>
<p id="note" style="color: #dc3545">Grey, because the rule is important.</p>
</div>
''',
extensions: const [
CssStylesheetHtmlExtension(), // must come first
CssBorderRadiusHtmlExtension(),
],
),
),
);
}
void main() => runApp(const ExampleApp());
Passing a stylesheet in #
A <link rel="stylesheet"> is never fetched. Read the file yourself and pass the text:
import 'package:flutter/services.dart' show rootBundle;
Future<Widget> buildCard() async {
final css = await rootBundle.loadString('assets/app.css');
return Html(
data: '<div class="card">Card</div>',
extensions: [CssStylesheetHtmlExtension(stylesheet: css)],
);
}
The argument is read before the document's own <style> blocks, which is where a
browser reads a <link> in <head>. A <style> rule of equal specificity therefore
wins.
Set readStyleElements: false to use the argument alone.
The cascade #
Per property, an !important declaration wins first, then the inline style, then the
higher specificity, then the later rule. The
selector and specificity reference
has the full tables.
What renders nothing #
Html(
data: '<style>@media (min-width: 600px) { p { color: red } }</style><p>x</p>',
extensions: [
CssStylesheetHtmlExtension(
onUnsupportedRule: (rule) => debugPrint('${rule.text}: ${rule.detail}'),
),
],
);
Without the callback that rule is dropped in silence. With it, each at-rule, pseudo-element and unevaluable pseudo-class is named once per document.
A @media block is also a defect in flutter_html itself. Its parseExternalCss()
throws a LateInitializationError on one, and a TypeError on @keyframes, with no
extension registered at all. This extension empties each <style> element after reading
it, which is what keeps parseExternalCss() away from the at-rule. Set
consumeStyleElements: false to leave flutter_html's own <style> handling in place,
and then keep both at-rules out of the document.
Registration order #
Every extension that reads or writes the inline style acts at the preStyling step, and
flutter_html runs them in registration order. List this one first:
Html(
data: '<style>.card { border-width: 2px; border-style: solid }</style>'
'<div class="card">Card</div>',
extensions: const [
CssStylesheetHtmlExtension(), // first
CssBorderHtmlExtension(), // then the border longhands resolve
],
);
Reverse those two and the border extension reads the style attribute before the rule
arrives in it. It finds no border, returns, and flutter_html drops both longhands as
usual. The text still renders. The border does not, and there is no error and no
warning.