A default action welded to a menu of alternatives. Click the label, or the caret
The label fires the default directly. The ▾ caret opens the menu: Enter / ↓ opens, ↑↓ move, Enter runs (and that choice becomes the new default), Esc closes.
Compose · reply
Send now (split button)
Duplicate (split button)
Working tree
Commit (split button)
Delete (split button)
Disabled: the WHOLE control, always (a split never half-disables)
Send (split button)
Commit (split button)
Last action: none yet · defaults: send send, commit commit · pointer + keyboard both work; the island is live.
Shared fill across both segments, following the Button emphasis ladder. One primary per view.
size
'sm' | 'md'
'md'
Control height + padding; the caret is a square of the same height.
leadingIcon
string
n/a
Optional glyph before the default label on the primary segment.
disabled
boolean
false
The WHOLE control inert and clearly seen (never faded to invisible). A split never half-disables: both segments always share one state.
Parameter
Type
Default
Description
leadingButton *
@Composable -> Unit
n/a
The default-action segment. Wrap SplitButtonDefaults.LeadingButton(onClick = …). Rounds the leading corners.
trailingButton *
@Composable -> Unit
n/a
The caret segment: SplitButtonDefaults.TrailingButton(checked, onCheckedChange) toggling a DropdownMenu. Rounds the trailing corners.
modifier
Modifier
Modifier
External modifier applied to the two-segment layout.
spacing
Dp
SplitButtonDefaults.Spacing
The hairline gap between the two segments (the divider seam).
expanded
Boolean
false
State you hold for the menu; bind to the trailing button’s checked and the DropdownMenu’s expanded.
enabled
Boolean
true
Passed to BOTH inner buttons together, because a split never half-disables (the whole control shares one state).
Menu(primaryAction:) is the native split button: the label fires primaryAction,
the attached caret opens the menu. State (isEnabled, pressed) comes from the
SwiftUI environment; styling is the shared .nockerl*ButtonStyle.
Style
Type
Default
Description
content
@ViewBuilder
n/a
The menu body: Button/Label rows, Section, Divider(); a leading systemImage: "checkmark" marks the current default.
label
@ViewBuilder
n/a
The primary segment’s content (the default action’s Label).
primaryAction
() -> Void
n/a
Fired by the primary segment directly, which is the split-button behaviour. Without it, the whole control is a plain Menu (no default action).
.menuStyle(.button)
MenuStyle
n/a
Renders the trigger as a button with the attached caret (the welded two-segment look).
.buttonStyle(.nockerlPrimary)
ButtonStyle
n/a
Shared fill. Swap for .nockerlSecondary / .nockerlDestructive to match the variant ladder.
Cross-platform drift (tracked for reconciliation)
This page designs the split button from the button + menu vocabulary rather than
transcribing a packaged native surface, so the platform tabs differ in how close each
one gets:
Compose: the Kotlin tab shows Material 3’s SplitButtonLayout as the canonical target; the app-side idiom it supersedes is a hand-rolled Row(NockerlButton + NockerlIconButton) + DropdownMenu.
Swift: the Swift tab documents Menu(primaryAction:), the actual split-button idiom, where a plain Menu { … } carries no default-action segment.
vs. its neighbours: use Button for a single action, Menu when there is no default action (the trigger only opens a popover), and Fab for the floating circular action + speed-dial. Split button is the JOIN: a default action welded to a menu of variants.