flutter_html_css_all 0.2.0 copy "flutter_html_css_all: ^0.2.0" to clipboard
flutter_html_css_all: ^0.2.0 copied to clipboard

Re-exports twenty-one flutter_html companion packages, nineteen of them CSS extensions, and returns a nineteen-entry extension list in the one order all their rules allow.

example/example.md

Example #

flutter_html 3.0.0 parses 51 CSS properties and drops the rest. This package re-exports twenty-one companion packages: nineteen add properties it drops or correct ones it mis-reads, one renders an <img> it declines, and one is the class-to-CSS plumbing a utility-class extension builds on. BundledCssExtensions registers nineteen instances. Each package states its own registration rules, and most of those rules fail silently: the document renders, one property does not apply, and nothing is logged. This package registers the nineteen in one order that satisfies every rule.

import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:flutter_html_css_all/flutter_html_css_all.dart';

class ExampleApp extends StatelessWidget {
  const ExampleApp({super.key});

  @override
  Widget build(BuildContext context) => MaterialApp(
        home: Scaffold(
          body: SingleChildScrollView(
            child: Html(
              data: '''
                <div style="border-width: 2px; border-style: solid;
                            border-color: #0d6efd; border-radius: 12px;
                            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
                            overflow: hidden; width: 50%; max-width: 320px;
                            padding: 12px; margin: 8px;">
                  <p style="font-size: 1.5rem; letter-spacing: 0.1em;">
                    A rounded, clipped card.
                  </p>
                  <p style="text-overflow: ellipsis; white-space: nowrap;">
                    A teaser that has to end somewhere.
                  </p>
                  <p style="text-transform: uppercase; background: #eef;
                            letter-spacing: 1pt;">
                    Absolute units, a background shorthand and a transform.
                  </p>
                </div>

                <div style="display: flex; gap: 8px; margin: 8px;">
                  <p style="flex: 1 1 auto;">One</p>
                  <p style="flex: 0 0 80px;">Two</p>
                </div>

                <img src="asset:photo.png" width="240" height="80"
                     style="object-fit: cover; object-position: 50% 20%;">
              ''',
              extensions: const BundledCssExtensions(),
            ),
          ),
        ),
      );
}

void main() => runApp(const ExampleApp());

With a stylesheet #

CssStylesheetHtmlExtension is off by default, because it takes over the cascade for <style> blocks and empties each block it reads. Pass an instance and it goes first of all, so every rule reaches the inline style before any other extension reads the attribute.

Html(
  data: '''
    <style>
      .card { border-radius: 12px; box-shadow: 0 4px 12px #0005; }
      .card p { letter-spacing: 0.1em; }
    </style>
    <div class="card"><p>Rounded, and spaced out.</p></div>
  ''',
  extensions: const BundledCssExtensions(
    stylesheetExtension: CssStylesheetHtmlExtension(),
  ),
);

With a utility-class extension #

BundledCssExtensions takes yours and registers it after the stylesheet extension and before every extension that reads the inline style. Add the package to your own pubspec.yaml; this one depends on neither, because a reader picks one.

import 'package:flutter_html_bootstrap/flutter_html_bootstrap.dart';

Html(
  data: '<div class="rounded-3 border p-3 text-bg-primary">Card</div>',
  extensions: const BundledCssExtensions(
    utilityClassExtension: BootstrapUtilitiesHtmlExtension(),
  ),
);

With a TagExtension #

A custom tag is not one of the 70 tags flutter_html renders. Without a TagExtension the element and its whole subtree render nothing. It goes in after, never inside the nineteen.

Html(
  data: '<v-card style="box-shadow: 0 4px 12px #0005;">Card</v-card>',
  extensions: BundledCssExtensions(
    after: [
      TagExtension(tagsToExtend: const {'v-card'}, child: const Text('Card')),
    ],
  ),
);

With an ImageExtension #

An ImageExtension is the one thing that goes in before. The last entry of the list is ImgAltHtmlExtension, which claims an <img> every image renderer declined. An ImageExtension with a custom assetSchema renders a source the built-in renderer declines, so registered after the bundle it loses that image to the alt text. There is no error and no warning.

Html(
  data: '<img src="bundle:photo.png" alt="Sales, 2026">',
  extensions: BundledCssExtensions(
    before: [
      ImageExtension(
        assetSchema: 'bundle:',
        builder: (context) =>
            Image.asset(context.attributes['src']!.replaceFirst('bundle:', '')),
      ),
    ],
  ),
);

const BundledCssExtensions(imgAlt: false) drops the alt-text extension instead.

At a fixed point inside the list #

An extension that has to run between two bundled ones goes in through an Insert. The anchor is a class. An anchor that is not in the list throws an ArgumentError when the list is read, so a typo cannot land the entry at the end.

Html(
  data: '<div style="border-radius: 12px;">Card</div>',
  extensions: const BundledCssExtensions(
    inserts: [
      Insert.before(CssBorderRadiusHtmlExtension, [MyCornerExtension()]),
    ],
  ),
);

const BundledCssExtensions.insertedBefore(CssBorderRadiusHtmlExtension, [MyCornerExtension()]) is the sugar for one point.

Arguments #

const BundledCssExtensions(
  rootFontSize: 16, // the rem basis, for font-size, line-height and max-width
  viewportSize: Size(400, 300), // what vw, vh, vmin and vmax resolve against
  resolvePercentages: false, // turn off under an IntrinsicHeight
  extraCursors: {'zoom-in': SystemMouseCursors.zoomIn},
  imgAlt: false, // drop the alt-text extension
  imageNetworkDomains: {'cdn.example.com'},
);

bundledCssExtensions() is the same arguments as a function call, for the sites where that reads better.

Anything beyond these means building the list by hand from the re-exported classes. doc/order.md states what such a list has to respect.