buildFluentSplitButton function
Widget
buildFluentSplitButton(
- FluentSplitButtonBaseState state,
- FluentSplitButtonStyle style,
- Set<
WidgetState> states, { - required FluentSplitButtonSide side,
Renders one half of a split button from a resolved state and style.
One half rather than the pair, because the halves are siblings with
independent interaction states: nesting one interaction surface inside the
other would make hovering the chevron light up the primary action too.
FluentSplitButton calls this twice, once per side, and lays the results
out in a row.
Takes FluentSplitButtonBaseState rather than FluentSplitButtonState on purpose: it never reads appearance, size or shape, so a consumer can supply their own style and still use Fluent's rendering.
states is the live interaction set of this half from
FluentInteractive.
Implementation
Widget buildFluentSplitButton(
FluentSplitButtonBaseState state,
FluentSplitButtonStyle style,
Set<WidgetState> states, {
required FluentSplitButtonSide side,
}) => Builder(
// The reading direction is read here rather than taken as an argument: the
// pair mirrors under RTL and every caller would otherwise have to know that.
builder: (BuildContext context) {
final roundsLeft = _roundsLeft(side, Directionality.of(context));
final button = style.button ?? const FluentButtonStyle();
final radius =
button.borderRadius?.resolve(states) ?? FluentRadius.allMedium;
final borderWidth =
button.borderWidth?.resolve(states) ?? FluentStroke.none;
final borderColor = button.borderColor?.resolve(states);
final dividerColor = style.dividerColor?.resolve(states);
// Each half keeps only the two corners on its own outer edge. The inner
// edge is square, which is what makes the pair read as one container.
final halfRadius = roundsLeft
? BorderRadius.only(
topLeft: radius.topLeft,
bottomLeft: radius.bottomLeft,
)
: BorderRadius.only(
topRight: radius.topRight,
bottomRight: radius.bottomRight,
);
// The border is painted by FluentSplitButtonEdgePainter, not by the half's
// own decoration: three sides plus two rounded corners is not a shape
// BoxDecoration can express — Flutter requires a uniform border before it
// will accept a border radius.
final half = buildFluentButton(
state.half(side),
button.copyWith(
borderRadius: WidgetStatePropertyAll<BorderRadius?>(halfRadius),
borderWidth: const WidgetStatePropertyAll<double?>(FluentStroke.none),
),
states,
);
// No rule means nothing for the painter to draw: every appearance Figma
// leaves undivided — subtle, transparent, and primary while disabled — is
// also unbordered, so the half's own decoration is the whole picture.
if (dividerColor == null) return half;
return FluentAnimatedStyle<FluentSplitButtonEdgeColors>(
// Upstream transitions `border` alongside `background` and `color` on the
// button root, at the same duration and curve — the divider is part of
// that border, so it tweens with the surface rather than snapping.
value: FluentSplitButtonEdgeColors(
// borderWidth is zero on exactly the appearances whose borderColor is
// null, so this stand-in is never painted. Keeping the pair non-null
// keeps the tween a single value rather than two nested animations.
border: borderColor ?? dividerColor,
divider: dividerColor,
),
spec: FluentMotionSpec.buttonSurface,
lerp: FluentSplitButtonEdgeColors.lerp,
builder: (_, colors) => CustomPaint(
foregroundPainter: FluentSplitButtonEdgePainter(
side: side,
borderColor: colors.border,
borderWidth: borderWidth,
dividerColor: colors.divider,
radius: halfRadius,
roundsLeft: roundsLeft,
),
child: half,
),
);
},
);