buildFluentDivider function

Widget buildFluentDivider(
  1. FluentDividerBaseState state,
  2. FluentDividerStyle style,
  3. Set<WidgetState> states
)

Renders a divider from a resolved state and style.

The third of the three-function recomposition contract. Takes FluentDividerBaseState rather than FluentDividerState on purpose: it never reads appearance or inset, so a consumer can supply their own style and still use Fluent's rendering.

states exists for symmetry with the interactive components and is always empty in practice — a divider has no interaction states to report. It is still honoured, so a caller passing a state-dependent style gets what they asked for.

Implementation

Widget buildFluentDivider(
  FluentDividerBaseState state,
  FluentDividerStyle style,
  Set<WidgetState> states,
) {
  final lineColor = style.lineColor?.resolve(states);
  final thickness = style.lineThickness?.resolve(states) ?? FluentStroke.thin;
  final shortLength = style.shortLineLength?.resolve(states) ?? FluentSpacing.s;
  final contentColor = style.contentColor?.resolve(states);
  final textStyle = style.textStyle?.resolve(states);
  final padding = style.padding?.resolve(states) ?? EdgeInsets.zero;
  final contentPadding =
      style.contentPadding?.resolve(states) ?? EdgeInsets.zero;
  final contentMinimum = style.contentMinimumSize?.resolve(states) ?? Size.zero;
  final gap = style.gap?.resolve(states) ?? FluentSpacing.sNudge;
  final iconSize = style.iconSize?.resolve(states) ?? FluentSize.size200;
  final vertical = state.vertical;

  // A hairline that stretches on one axis and is pinned on the other. The
  // colour is only painted when a token was actually resolved: an absent colour
  // must leave the rule unpainted rather than fall back to a literal
  // transparent, which would stay invisible in high contrast where Fluent's
  // transparent tokens turn opaque.
  Widget rule({required bool flexible}) {
    final line = SizedBox(
      width: vertical ? thickness : (flexible ? null : shortLength),
      height: vertical ? (flexible ? null : shortLength) : thickness,
      child: lineColor == null ? null : ColoredBox(color: lineColor),
    );
    return flexible ? Expanded(child: line) : line;
  }

  Widget? content;
  if (state.hasContent) {
    Widget row = Row(
      mainAxisSize: MainAxisSize.min,
      mainAxisAlignment: MainAxisAlignment.center,
      spacing: state.icon != null && state.label != null ? gap : 0,
      children: <Widget>[
        if (state.icon != null)
          IconTheme.merge(
            data: IconThemeData(color: contentColor, size: iconSize),
            child: state.icon!,
          ),
        if (state.label != null) state.label!,
      ],
    );
    if (textStyle != null || contentColor != null) {
      row = DefaultTextStyle.merge(
        style: (textStyle ?? const TextStyle()).copyWith(color: contentColor),
        child: row,
      );
    }
    content = ConstrainedBox(
      constraints: BoxConstraints(
        minWidth: contentMinimum.width,
        minHeight: contentMinimum.height,
      ),
      child: Padding(padding: contentPadding, child: row),
    );
  }

  // With no label there is nothing to align around, so the single rule spans the
  // whole divider whatever `alignment` says.
  final children = content == null
      ? <Widget>[rule(flexible: true)]
      : <Widget>[
          rule(flexible: state.alignment != FluentDividerAlignment.start),
          content,
          rule(flexible: state.alignment != FluentDividerAlignment.end),
        ];

  return Padding(
    padding: padding,
    child: vertical
        ? Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: children,
          )
        : Row(mainAxisAlignment: MainAxisAlignment.center, children: children),
  );
}