flutterbits

Styling with .tw

The accumulator model — chain typed utilities onto any widget; they collect into one immutable style with last-wins conflict resolution.

.tw begins a style chain on any widget. Each call adds a typed utility; the chain accumulates into one immutable FwStyle and renders as a single styled node.

final t = context.fw;
Text('Save').tw.px(4).py(2).bg(t.colors.primary).text(t.colors.primaryForeground).rounded(t.radii.md);

Declarative styling

In plain Flutter, styling is imperative — you assemble the widget tree by hand, choosing each wrapper and its nesting order:

// Imperative — you build the structure.
Padding(
  padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
  child: DecoratedBox(
    decoration: BoxDecoration(color: t.colors.primary, borderRadius: BorderRadius.circular(6)),
    child: child,
  ),
);

.tw makes styling declarative: you declare what you want and the engine resolves how — which primitives to emit, in what order, and how to handle conflicts. The chain above is just:

// Declarative — you declare intent.
child.tw.px(4).py(2).bg(t.colors.primary).rounded(6);

The chain is only syntax; semantically it's an unordered set of declarations. Order doesn't change the structure — only conflicts resolve by order (last-wins, below). That's the same shift Tailwind made for the web: stop writing the how (hand-built rules/wrappers), declare the what (utilities).

The accumulator model

A .tw chain isn't a stack of wrappers — the utilities merge into one style that resolves lazily. Two things follow from that:

  • Last-wins conflict resolution. If you set the same property twice, the last call wins. .px(4).px(2) resolves to horizontal padding of 2 units (8 px), not 6 — and emits no duplicate wrapper.
  • Lazy resolution. The style resolves against the active interaction states and the viewport/container size at build time, which is what makes states (hover:/focus:/…) and breakpoints (sm:/md:/…) first-class rather than bolted on.

Units

Spacing and sizing are in utility units of 4 logical pixels, exactly like Tailwind's default scale. p(4)p-4 → 16 px. w(10) → 40 px.

The utility families

Every single-box utility lives on .tw. They're documented in full in the Utility reference; here's the map (multi-child layout — direction, gap, positioning — is not .tw; see Layout):

Composing it

final t = context.fw;
Text('Card title')
    .tw
    .textSize(FwFontSize.lg.px)
    .weight(600)
    .text(t.colors.cardForeground); // a styled Text

const SizedBox()
    .tw
    .w(64)
    .h(40)
    .bg(t.colors.muted)
    .rounded(t.radii.lg)
    .shadow(t.shadows.sm); // a styled box

Next steps

On this page