flutter_html_css_flex

pub version pipeline license GitLab

A flutter_html extension that lays out CSS flexbox containers, single-axis grid containers and gap. flutter_html 3.0.0 renders none of the three. Its parser maps display: flex to display: inline, so a flex container renders as inline text. This extension replaces the container's build instead of wrapping it. No released utility-class extension recognises it yet.

Contents

Features

  • The extension lays out display: flex, inline-flex, grid and inline-grid containers.
  • It repairs Style.display, which flutter_html sets to inline for all four values. It repairs it for another nineteen keywords it does not lay out as well, from table to contents, so those stop demoting a block element to inline.
  • It reads flex-direction, flex-flow, flex-wrap, justify-content, align-items and align-content on the container.
  • It reads flex, flex-grow, flex-basis, align-self and order on each item.
  • It reads gap, row-gap and column-gap in px, em and rem. On the container, rem resolves against the container's own font size, not the root.
  • It reads grid-template-columns with <length>, <flex>, auto and repeat().
  • It keeps the element's box: margin, padding, border, background, width, height and id anchors still work.
  • The property map is in doc/properties.md.

Getting started

dependencies:
  flutter_html: ^3.0.0
  flutter_html_css_flex: ^0.3.0

Usage

import 'package:flutter_html/flutter_html.dart';
import 'package:flutter_html_css_flex/flutter_html_css_flex.dart';

Html(
  data: '''
    <div style="display: flex; gap: 16px; align-items: center">
      <div style="flex: 0 0 120px">Sidebar</div>
      <div style="flex-grow: 1">Body</div>
    </div>
  ''',
  extensions: const [CssFlexHtmlExtension()],
);

Before you register it

This extension matches on the inline style, never on the tag name. It cannot make flutter_html render a tag that has no renderer. A TagExtension does that.

A custom tag such as <v-row> needs a TagExtension to become an element with children at all. List this extension first:

Html(
  data: '<v-row style="display: flex; gap: 8px"><p>One</p><p>Two</p></v-row>',
  extensions: [
    const CssFlexHtmlExtension(), // must come first
    TagExtension(tagsToExtend: const {'v-row'}, child: const SizedBox.shrink()),
  ],
);

flutter_html builds an element with the first extension in the list that matches it. Listed second, the TagExtension's widget renders and the layout is lost. There is no error and no warning.

List flutter_html_css_size_constraints 0.1.0 before this extension. That extension hands the element on to the next match; this one does not. Listed after it, max-width, min-width, max-height and min-height never apply. There is no error and no warning.

Html(
  data: '<div style="display: flex; max-width: 320px"><p>One</p><p>Two</p></div>',
  extensions: const [
    CssFlexHtmlExtension(),
    CssSizeConstraintsHtmlExtension(), // never runs; max-width never applies
  ],
);

Order against a utility-class extension does not matter. This extension reads the inline style at the preProcessing and building steps, both after such an extension expands its classes.

Released flutter_html_bootstrap 0.1.1 and flutter_html_vuetify 0.1.0 suppress display: flex, and they are right to. Unsuppressed, it reaches flutter_html as display: inline and breaks the page. Neither recognises CssFlexHtmlExtension, and a companion registry entry alone is not enough. Both suppress display by value, so each needs a code change first. That change is written in both and not released. Until those two releases, write the layout into an inline style attribute. Once they ship, d-flex, d-inline-flex, flex-*, justify-*, align-*, order-* and the gap classes render next to this extension. d-table and its variants stay suppressed. That is still right: without the declaration a <div> keeps its own block box. Every other class those extensions handle keeps working next to this one.

Limitations

  • A repaired keyword gets its box level back and nothing more. There is no table layout, and table-column renders nothing at all, as a column box does in CSS.
  • !important is not read. display: flex !important is not recognised, and the container renders as inline text. There is no error and no warning.
  • An inline-flex container, and any container under Html(shrinkWrap: true), has no free main-axis space. flex-grow and justify-content do nothing there. There is no error and no warning.
  • A percentage flex-basis inside a nested container measures as zero. Use a length there. There is no error and no warning.
  • flex-shrink parses, and the layout does not act on it. Flutter's flex layout has no shrink phase.
  • flex-grow matches CSS for flex: 1 1 0%. It diverges when two growing items hold different amounts of content.
  • A wrapping container takes no growth factors and no per-item cross alignment. Flutter's Wrap has neither.
  • <a>, <img>, <details> and <ruby> keep their own renderer. Put the container on a wrapping <div>.
  • Most of CSS Grid is absent. A grid-template-columns this package cannot read falls back to a single column. There is no error and no warning.
  • em and rem do not resolve against the document root. A rem length takes the container's font size instead. flutter_html_css_font_size will close this gap.
  • A calc() or var() value resolves to nothing, and the property keeps its previous value. <style> blocks and stylesheets never reach this extension.

Full list: doc/limitations.md.

Additional information

Issues and merge requests go to the GitLab repository. The building step sibling that must come first is flutter_html_css_size_constraints.

CI, publishing and Renovate are described in CONTRIBUTING.md.