resolveFluentSpinnerStyle function
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 colour comes from a Fluent token; nothing here computes one.
Token sources are the Figma Spinner and .SpinnerBase component sets,
extracted into test/fixtures/spinner.json and
test/fixtures/spinner_base.json and asserted variant-by-variant in the
tests.
Implementation
FluentSpinnerStyle resolveFluentSpinnerStyle(
FluentSpinnerState state,
FluentThemeData theme,
) {
final c = theme.colors;
// Figma binds these three on the Track ellipse, the Tail ellipse and the
// label TEXT of each Spinner variant.
final (track, indicator, label) = switch (state.appearance) {
FluentSpinnerAppearance.primary => (
c.brandStroke2Contrast,
c.brandStroke1,
c.neutralForeground1,
),
FluentSpinnerAppearance.subtle => (
c.neutralStrokeAlpha2,
c.neutralStrokeOnBrand2,
c.neutralForegroundStaticInverted,
),
};
// Geometry, verbatim from the Figma .SpinnerBase set. Thickness there is
// authored as an integer-percent `innerRadius`, so the rendered values land
// 0.04 either side of the stroke ramp; the ramp stop is what ships. See
// doc/token-divergences.md for extraTiny, the one that misses by more.
final (diameter, strokeWidth, textStyle) = switch (state.size) {
FluentSpinnerSize.extraTiny => (
16.0,
FluentStroke.thick,
theme.typography.body1,
),
FluentSpinnerSize.tiny => (
20.0,
FluentStroke.thick,
theme.typography.body1,
),
FluentSpinnerSize.extraSmall => (
24.0,
FluentStroke.thick,
theme.typography.body1,
),
FluentSpinnerSize.small => (
28.0,
FluentStroke.thick,
theme.typography.body1,
),
FluentSpinnerSize.medium => (
32.0,
FluentStroke.thicker,
theme.typography.subtitle2,
),
FluentSpinnerSize.large => (
36.0,
FluentStroke.thicker,
theme.typography.subtitle2,
),
FluentSpinnerSize.extraLarge => (
40.0,
FluentStroke.thicker,
theme.typography.subtitle2,
),
FluentSpinnerSize.huge => (
44.0,
FluentStroke.thickest,
theme.typography.subtitle1,
),
};
return FluentSpinnerStyle(
trackColor: WidgetStatePropertyAll<Color?>(track),
indicatorColor: WidgetStatePropertyAll<Color?>(indicator),
labelColor: WidgetStatePropertyAll<Color?>(label),
strokeWidth: WidgetStatePropertyAll<double?>(strokeWidth),
diameter: WidgetStatePropertyAll<double?>(diameter),
textStyle: WidgetStatePropertyAll<TextStyle?>(textStyle),
// Figma expresses the gap as padding on the Label frame rather than as
// auto-layout item spacing, bound to `Spacing/Horizontal/S` on every one of
// the 64 variants — the vertical layouts included.
gap: const WidgetStatePropertyAll<double?>(FluentSpacing.s),
);
}