Button Group
A group of buttons: a few actions of the same rank, drawn as one control or set apart at one distance.
import com.pantrixui.components.elements.buttongroup.PantrixButtonGroup
- Attached or apart. With
attachedon, which is its default, the buttons touch: the group has one shape, one edge around it and a line between each two buttons. With it off, the same buttons stand with a gap between them, each with its own shape and edge. - Row or column.
orientationlays the buttons out side by side or one under another. In a column they are all as wide as the widest. - Width. In a row each button is as wide as what it says. In a narrow space the short ones stay whole and the long ones share what is left; with
equalWidththey all share the group's width equally. - A label or an icon alone.
GroupButtonhas two constructors: a label, with or without an icon before it; or an icon alone. For a button that is an icon alone,contentDescriptionis required. - Each button is a button. Tab stops at each of them, a screen reader says the name and the role of each, and each draws its own focus ring and reports its own press.
- A button's own state.
enabled,loading,variantandcolorscan be given to eachGroupButtonseparately. The group'svariant,size,colorsandenabledare for all the buttons. - Automation. The buttons are named inside the group's tag:
ButtonGroup_clipboard.Button_copy. A button that is an icon alone is tagged as anIconButton, and a button without a key by its index.
Examples
An attached toolbar
Its shortest form: a list of buttons. The group is attached and its variant is Outline: there is one edge around it and a line between each two buttons, and the corners are only at the group's two ends.
The first button has an icon before its label; the last is an icon alone and says what it does with contentDescription. Each button is a button in its own right: Tab stops at each of them, and a screen reader says the name of each. Press a button: the line below names the last action.
var last by rememberSaveable { mutableStateOf<String?>(null) }
PantrixButtonGroup(
buttons = listOf(
GroupButton(text = "Copy", onClick = { last = "Copy" }, icon = PantrixIcons.Copy, tagKey = "copy"),
GroupButton(text = "Paste", onClick = { last = "Paste" }, tagKey = "paste"),
GroupButton(
icon = PantrixIcons.MoreHorizontal,
contentDescription = "More actions",
onClick = { last = "More actions" },
tagKey = "more"
)
),
tagKey = "clipboard"
)
PantrixText(
text = last?.let { "Last action: $it" } ?: "No action pressed yet",
style = PantrixTheme.typography.footnote,
color = PantrixTheme.colors.textSecondary,
tagKey = "clipboard_result"
)
A stepper of icons alone
A stepper made of icons alone, in the Soft variant. A button that is an icon alone is written with the second constructor of GroupButton, and there contentDescription is required: a button is never decoration.
enabled is given to each button separately: at the lower limit the minus button cannot be pressed, and the plus button can.
var count by rememberSaveable { mutableIntStateOf(1) }
Row(
horizontalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.md),
verticalAlignment = Alignment.CenterVertically
) {
PantrixButtonGroup(
buttons = listOf(
GroupButton(
icon = PantrixIcons.Minus,
contentDescription = "Decrease",
onClick = { count-- },
enabled = count > 1,
tagKey = "minus"
),
GroupButton(
icon = PantrixIcons.Plus,
contentDescription = "Increase",
onClick = { count++ },
tagKey = "plus"
)
),
variant = ButtonVariant.Soft,
tagKey = "stepper"
)
PantrixText(text = "Quantity: $count", tagKey = "stepper_count")
}
Labels read by their description
Two buttons that change the text size. Their labels are "A-" and "A+": enough for someone who sees them, but they mean nothing in a screen reader. So each has a contentDescription, and the screen reader reads the description alone, not the label: "Make text smaller", "Make text larger".
var percent by rememberSaveable { mutableIntStateOf(100) }
Row(
horizontalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.md),
verticalAlignment = Alignment.CenterVertically
) {
PantrixButtonGroup(
buttons = listOf(
GroupButton(
text = "A-",
onClick = { percent -= 10 },
enabled = percent > 80,
contentDescription = "Make text smaller",
tagKey = "smaller"
),
GroupButton(
text = "A+",
onClick = { percent += 10 },
enabled = percent < 150,
contentDescription = "Make text larger",
tagKey = "larger"
)
),
tagKey = "text_size"
)
PantrixText(text = "Text size: $percent%", tagKey = "text_size_percent")
}
Variants
variant is the variant of every button that has none of its own; when it is not given, it is Outline. In the variants that have an edge, Outline and Subtle, there is one edge around the group and the lines between the buttons are in its colour. In those without an edge, only the lines between the buttons are drawn.
listOf(
ButtonVariant.Outline,
ButtonVariant.Subtle,
ButtonVariant.Soft,
ButtonVariant.Solid,
ButtonVariant.Text
).forEach { variant ->
PantrixButtonGroup(
buttons = listOf(
GroupButton(text = "Previous", onClick = {}, tagKey = "previous"),
GroupButton(text = "Next", onClick = {}, tagKey = "next")
),
variant = variant,
tagKey = "variant_${variant.name}"
)
}
Sizes
size is the size of every button in the group, so that they all line up: here Small, Medium and Large. When it is not given, it is Medium.
listOf(ButtonSize.Small, ButtonSize.Medium, ButtonSize.Large).forEach { size ->
PantrixButtonGroup(
buttons = listOf(
GroupButton(text = "Cut", onClick = {}, tagKey = "cut"),
GroupButton(text = "Copy", onClick = {}, tagKey = "copy"),
GroupButton(text = "Paste", onClick = {}, tagKey = "paste")
),
size = size,
tagKey = "size_${size.name}"
)
}
A column of actions
orientation = Orientation.Vertical lays the buttons out one under another: they are all as wide as the widest. In an attached column the corners are at the top and the bottom of the group, and the lines between the buttons are horizontal.
The action that deletes carries its own variant: on that button the variant of GroupButton takes the place of the group's.
PantrixButtonGroup(
buttons = listOf(
GroupButton(text = "Rename", onClick = {}, tagKey = "rename"),
GroupButton(text = "Duplicate", onClick = {}, tagKey = "duplicate"),
GroupButton(text = "Delete", onClick = {}, variant = ButtonVariant.Destructive, tagKey = "delete")
),
orientation = Orientation.Vertical,
tagKey = "record"
)
Apart and of equal width
The two actions under a dialog. With attached = false they stand apart, each with its own shape and edge, with the style's gap between them. With equalWidth = true they share the group's width equally; here the group is 360dp wide at most.
loading is the button's own state: when "Delete" is pressed it shows as loading for a short while and cannot be pressed in the meantime. "Cancel" stops the loading.
var deleting by remember { mutableStateOf(false) }
LaunchedEffect(deleting) {
if (deleting) {
delay(1500)
deleting = false
}
}
PantrixButtonGroup(
buttons = listOf(
GroupButton(text = "Cancel", onClick = { deleting = false }, tagKey = "cancel"),
GroupButton(
text = "Delete",
onClick = { deleting = true },
loading = deleting,
variant = ButtonVariant.Destructive,
tagKey = "delete"
)
),
modifier = Modifier.widthIn(max = 360.dp),
attached = false,
equalWidth = true,
tagKey = "footer"
)
In a narrow space
The same three buttons, twice, in a space 280dp wide. In a row each button is as wide as what it says; where there is not enough room the short ones stay whole and the long one takes what is left, its label cut off with an ellipsis.
The second group has equalWidth = true: the three buttons share the width equally, and the long label is cut off sooner.
PantrixButtonGroup(
buttons = listOf(
GroupButton(text = "Save", onClick = {}, tagKey = "save"),
GroupButton(text = "Exit without saving and delete the draft", onClick = {}, tagKey = "discard"),
GroupButton(text = "Cancel", onClick = {}, tagKey = "cancel")
),
modifier = Modifier.width(280.dp),
tagKey = "narrow"
)
PantrixButtonGroup(
buttons = listOf(
GroupButton(text = "Save", onClick = {}, tagKey = "save"),
GroupButton(text = "Exit without saving and delete the draft", onClick = {}, tagKey = "discard"),
GroupButton(text = "Cancel", onClick = {}, tagKey = "cancel")
),
modifier = Modifier.width(280.dp),
equalWidth = true,
tagKey = "narrow_equal"
)
In a role's colours
colors draws the group's buttons in the given colours instead of a variant's primary colours. Here PantrixColor.buttonColors(ButtonVariant.Outline) gives the outlined button colours of the success role. In an attached group the colour given for the edge is the colour of the group's one edge and of the lines between the buttons.
PantrixButtonGroup(
buttons = listOf(
GroupButton(text = "Approve", onClick = {}, icon = PantrixIcons.Check, tagKey = "approve"),
GroupButton(text = "Approve all", onClick = {}, tagKey = "approve_all")
),
colors = PantrixTheme.colors.success.buttonColors(ButtonVariant.Outline),
tagKey = "review"
)
In its own colour
The first group's colour is in none of the theme's roles: ButtonColors takes the fill, the content's colour and the edge directly.
In the second group only one button has a colour of its own. On that button the colors of GroupButton take the place of the group's colours and of its own variant; the button still stands inside the group's edge.
val own = ownColors()
PantrixButtonGroup(
buttons = listOf(
GroupButton(text = "Add to collection", onClick = {}, icon = PantrixIcons.Star, tagKey = "collect"),
GroupButton(text = "Share", onClick = {}, tagKey = "share")
),
colors = ButtonColors(container = own.container, content = own.onContainer, border = own.color),
tagKey = "own_colors"
)
PantrixButtonGroup(
buttons = listOf(
GroupButton(text = "Edit", onClick = {}, tagKey = "edit"),
GroupButton(
text = "Feature",
onClick = {},
colors = ButtonColors(container = own.color, content = own.onColor),
tagKey = "feature"
)
),
tagKey = "one_own_button"
)
Shape and style
In an attached group shape is the shape of the whole group: each button carries only the corners at its own end. In a group whose buttons stand apart it is the shape of each button. The first group was drawn in the theme's slightly rounded shape (shapes.sm).
style changes the group's geometry: in the second group ButtonGroupStyle(gap = 16.dp) widens the gap between the buttons that stand apart. The buttons' sizes come from the button's own style, not from the group's.
PantrixButtonGroup(
buttons = listOf(
GroupButton(text = "Print", onClick = {}, tagKey = "print"),
GroupButton(text = "Export", onClick = {}, tagKey = "export"),
GroupButton(text = "Share", onClick = {}, tagKey = "share")
),
shape = PantrixTheme.shapes.sm,
tagKey = "less_round"
)
PantrixButtonGroup(
buttons = listOf(
GroupButton(text = "Back", onClick = {}, tagKey = "back"),
GroupButton(text = "Next", onClick = {}, tagKey = "forward")
),
attached = false,
style = ButtonGroupStyle(gap = 16.dp),
tagKey = "wide_gap"
)
A disabled group
The group's enabled = false disables every button: none of them takes a press, and their labels and the group's edge are drawn faded.
PantrixButtonGroup(
buttons = listOf(
GroupButton(text = "Undo", onClick = {}, tagKey = "undo"),
GroupButton(text = "Redo", onClick = {}, tagKey = "redo")
),
enabled = false,
tagKey = "locked"
)
API
buttons
A group of buttons: a few actions of the same rank, drawn as one control or set apart at one distance.
| Parameter | Type | Default | What it is |
|---|---|---|---|
buttons | List<GroupButton> | the buttons, in order. An empty list draws nothing. | |
modifier | Modifier | Modifier | the group's modifier, outside the touch target an attached group reserves. |
variant | ButtonVariant | ButtonVariant.Outline | the variant of every button that has none of its own. |
size | ButtonSize | ButtonSize.Medium | the size of every button, so that they line up. |
orientation | Orientation | Orientation.Horizontal | Orientation.Horizontal for a row, Orientation.Vertical for a column of actions. |
attached | Boolean | true | true draws the buttons as one control. False sets them the style's gap apart. |
equalWidth | Boolean | false | true gives every button of a row the same share of the width the group is given; in a room without an end, the width of the widest. A column's buttons are as wide as each other whatever this says. |
enabled | Boolean | true | false disables every button. |
style | ButtonGroupStyle? | null | this call's own style: over every enclosing one and under the density. Only the fields it sets change, on every platform. The buttons keep the button's own style for their sizes. |
shape | Shape | PantrixTheme.style(ButtonGroupStyle, style).shape.toShape() | the group's shape: attached, the shape of the whole group; apart, of each button. |
colors | ButtonColors | variant.colors() | what the buttons are painted with, each one that has neither colours nor a variant of its own. Attached, the border's colour is the group's one edge, and the colour of the lines between the buttons. |
tagKey | String? | null | the group's automation key: the group is ButtonGroup_<tagKey>, and its buttons are named inside it, ButtonGroup_<tagKey>.Button_<key>, so that two groups with the same actions are told apart. |
ButtonGroupStyle
PantrixButtonGroup's geometry: the shape of the group, the line between two buttons that are attached, and the room between two that are not.
| Parameter | Type | Default | What it is |
|---|---|---|---|
shape | ShapeSpec | ShapeSpec.Unspecified | The group's shape: a step of the brand's shape scale, or a fixed shape. |
dividerWidth | Dp | Dp.Unspecified | The width of the line between two attached buttons. |
gap | Dp | Dp.Unspecified | The room between two buttons that are not attached. |
ButtonColors
What a button is painted with. ButtonVariant.colors gives a variant's in primary, and PantrixColor.buttonColors the same prominences in any role.
| Parameter | Type | Default | What it is |
|---|---|---|---|
container | Color | The fill; transparent for an outline or a text button. | |
content | Color | The label's and the icon's colour. | |
border | Color | Color.Unspecified | The edge's colour, or Color.Unspecified for a button without an edge. |
Accessibility
- Touch target. An attached group reserves the platform's touch target once, around itself; each of its buttons is at least that long along the group.