resolveFluentMessageBarStyle function
FluentMessageBarStyle
resolveFluentMessageBarStyle(
- FluentMessageBarState state,
- FluentThemeData theme
Resolves the default style for state against theme.
The second of the three-function recomposition contract, and the only one that reads the design axes. Every value comes from a Fluent token; nothing here computes a colour.
Token sources are the Figma Message bar component set and the
MessageBar status variable collection, extracted into
test/fixtures/message_bar.json — the intent triples are the fixture's
aliases block, one mode per intent, and they are asserted against the
named alias getters in the tests rather than against hex.
Three divergences from useMessageBarStyles.styles.ts, all resolved toward
Figma:
- the type ramp is
caption1Strongovercaption1(12/16), where React usesbody1Strongoverbody1(14/20); - the warning glyph takes
Status/Warning/Foreground/**1**/Rest, where React takescolorStatusWarningForeground3— identical in light, a full ramp step apart in dark; - the bar is padded
Spacing/Horizontal/Mon both sides, where React pads only the left and leans on the dismiss button's own box.
Implementation
FluentMessageBarStyle resolveFluentMessageBarStyle(
FluentMessageBarState state,
FluentThemeData theme,
) {
final c = theme.colors;
// Rest only. The Figma set has no State axis and React declares no hover,
// pressed, selected or disabled rule, so there is no second token to select
// between — but the ramp is still spelled through FluentStateColor so a
// consumer who adds one gets the usual precedence for free.
final (background, border, icon) = switch (state.intent) {
FluentMessageBarIntent.info => (
c.neutralBackground3,
c.neutralStroke1,
c.neutralForeground3,
),
FluentMessageBarIntent.success => (
c.statusSuccessBackground1,
c.statusSuccessBorder1,
c.statusSuccessForeground1,
),
FluentMessageBarIntent.warning => (
c.statusWarningBackground1,
c.statusWarningBorder1,
c.statusWarningForeground1,
),
// Error binds the DANGER family. The axis name and the token family differ.
FluentMessageBarIntent.error => (
c.statusDangerBackground1,
c.statusDangerBorder1,
c.statusDangerForeground1,
),
};
final radius = switch (state.shape) {
FluentMessageBarShape.rounded => FluentRadius.allMedium,
FluentMessageBarShape.square => BorderRadius.zero,
};
// The glyph and the body sit 2 and 4 below the top of the multi-line content
// row, which is (24 - 20) / 2 and (24 - 16) / 2 — i.e. both are centred
// against the 24-high dismiss button. They are expressed as insets rather
// than as CrossAxisAlignment.center because a body that wraps to three lines
// must keep its glyph at the FIRST line, which is the whole point of the
// multi-line layout.
final (iconInset, bodyInset) = state.multiline
? (
const EdgeInsets.only(top: FluentSpacing.xxs),
const EdgeInsets.only(top: FluentSpacing.xs),
)
: (EdgeInsets.zero, EdgeInsets.zero);
return FluentMessageBarStyle(
backgroundColor: FluentStateColor.tokens(rest: background),
foregroundColor: FluentStateColor.tokens(rest: c.neutralForeground1),
borderColor: FluentStateColor.tokens(rest: border),
borderWidth: const WidgetStatePropertyAll<double?>(FluentStroke.thin),
borderRadius: WidgetStatePropertyAll<BorderRadius?>(radius),
iconColor: FluentStateColor.tokens(rest: icon),
iconSize: const WidgetStatePropertyAll<double?>(FluentSize.size200),
// 12/16 semibold over 12/16 regular, verbatim from the two styled runs of
// the single Figma text node. React is a ramp step larger on both.
titleTextStyle: WidgetStatePropertyAll<TextStyle?>(
theme.typography.caption1Strong,
),
textStyle: WidgetStatePropertyAll<TextStyle?>(theme.typography.caption1),
padding: const WidgetStatePropertyAll<EdgeInsetsGeometry?>(
EdgeInsets.symmetric(
horizontal: FluentSpacing.m,
vertical: FluentSpacing.sNudge,
),
),
gap: const WidgetStatePropertyAll<double?>(FluentSpacing.s),
multilineGap: const WidgetStatePropertyAll<double?>(FluentSpacing.mNudge),
iconPadding: WidgetStatePropertyAll<EdgeInsetsGeometry?>(iconInset),
bodyPadding: WidgetStatePropertyAll<EdgeInsetsGeometry?>(bodyInset),
minimumSize: const WidgetStatePropertyAll<Size?>(Size(0, 36)),
// The dismiss button is a real FluentButton wearing the message bar's
// geometry: a 24 square with a 2px inset around a 20 glyph, which is
// narrower than any FluentButton size ramp produces on its own.
dismissButtonStyle: const FluentButtonStyle(
padding: WidgetStatePropertyAll<EdgeInsetsGeometry?>(
EdgeInsets.all(FluentSpacing.xxs),
),
iconSize: WidgetStatePropertyAll<double?>(FluentSize.size200),
minimumSize: WidgetStatePropertyAll<Size?>(
Size(FluentSize.size240, FluentSize.size240),
),
),
);
}