Skip to content

Expressive buttons in Jetpack Compose

Press and hold: the corners morph from round to square. Sizes XS, S, M and L shown at real dp.

Button has a new overload that takes shapes: ButtonShapes. Pass ButtonDefaults.shapes() and the button is round at rest and squarer while pressed, animated by the theme’s fast spatial spring. Without shapes you get the old fixed-shape button.

@Composable
fun MorphingButton(onClick: () -> Unit) {
// Passing `shapes` switches to the overload whose corners morph (round -> square) on press.
Button(onClick = onClick, shapes = ButtonDefaults.shapes()) { Text("Continue") }
}

FilledTonalButton, ElevatedButton, OutlinedButton and TextButton have the same overload.

Size Height Icon Constant
Extra small 32dp 20dp ButtonDefaults.ExtraSmallContainerHeight
Small (default) 40dp 20dp ButtonDefaults.MinHeight
Medium 56dp 24dp ButtonDefaults.MediumContainerHeight
Large 96dp 32dp ButtonDefaults.LargeContainerHeight
Extra large 136dp 40dp ButtonDefaults.ExtraLargeContainerHeight

For anything other than the default, size every part from the same height with the …For(height) helpers:

@Composable
fun MediumCta(icon: ImageVector, onClick: () -> Unit) {
// Sizes: ExtraSmall 32dp, Small 40dp (default), Medium 56dp, Large 96dp, ExtraLarge 136dp.
val height = ButtonDefaults.MediumContainerHeight
Button(
onClick = onClick,
modifier = Modifier.heightIn(height),
shapes = ButtonDefaults.shapesFor(height),
contentPadding = ButtonDefaults.contentPaddingFor(height, hasStartIcon = true),
) {
Icon(icon, contentDescription = null, modifier = Modifier.size(ButtonDefaults.iconSizeFor(height)))
Spacer(Modifier.size(ButtonDefaults.iconSpacingFor(height)))
Text("Start free trial", style = ButtonDefaults.textStyleFor(height))
}
}
  • One medium or large button for the main action of a screen, like “Start free trial” on a paywall.
  • Default size for everything else. Two large buttons side by side compete for attention.
  • Setting Modifier.height(56.dp) without shapesFor, contentPaddingFor and textStyleFor. The label and corners then stay sized for 40dp.
  • Passing shape = and expecting the press morph. The morph only comes with shapes =.

Text buttons need no extra semantics. Icon-only buttons belong in icon buttons.

Related: toggle buttons · button groups · motion springs

Want your agent to get this right every time? Install the skill. If this page helped,star the repo on GitHub.

Built by Sanikant. Set in Google Sans Flex (SIL Open Font License), icons from Material Symbols (Apache 2.0). Material Design is a Google trademark; this project is not affiliated with Google.