I want to create a Button
that while disabled, shows a CircularProgressIndicator
(use case: user clicks the button, a network request is made and during this the button is disabled with a nice spinner showing). The issue I have is that the CircularProgressIndicator
is not vertically centered. This is my current code:
@Preview
@Composable
private fun ButtonSandbox() {
Button(
enabled = false,
modifier = Modifier.fillMaxWidth()
.height(48.dp),
onClick = {},
colors = ButtonDefaults.buttonColors(
disabledContainerColor = MaterialTheme.colorScheme.primary,
disabledContentColor = MaterialTheme.colorScheme.onPrimary
)
) {
CircularProgressIndicator(
color = MaterialTheme.colorScheme.onPrimary,
trackColor = MaterialTheme.colorScheme.onPrimary, // just to make it completely visible in preview
modifier = Modifier.align(Alignment.CenterVertically) // my (failed) attempt to fix it
)
}
}
And this is how it looks like, note that the CircularProgressIndicator
is not vertically centered:
How can I fix that?
Button uses a Surface and a Row where the content is displayed. Row content is already aligned center vertically. If you look at the min height by default is 40.dp. Even if you don't provide the height modifier it should work
@Preview
@Composable
fun ButtonSandbox() {
Button(
enabled = false,
modifier = Modifier.fillMaxWidth(),
onClick = {},
colors = ButtonDefaults.buttonColors(
disabledContainerColor = MaterialTheme.colorScheme.primary,
disabledContentColor = MaterialTheme.colorScheme.onPrimary
)
) {
CircularProgressIndicator(
color = MaterialTheme.colorScheme.onPrimary,
trackColor = MaterialTheme.colorScheme.onPrimary,
)
}
}
If you want to customize the button height i suggest you use a Surface and a row and provide the height as required.
Something like the below code will help. Customize this to your needs.
val ButtonShape = RoundedCornerShape(250.dp)
@Composable
fun CustomRoundedButton(
modifier: Modifier = Modifier,
borderColor: Color? = null,
backgroundColor: Color = MaterialTheme.colorScheme.primary,
elevation: Dp = 0.dp,
content: @Composable () -> Unit,
onClick: () -> Unit
) {
val newModifier = if (borderColor != null) {
modifier.border(1.dp, borderColor, shape = ButtonShape)
} else {
modifier
}
val finalModifier = newModifier.height(48.dp)
Surface(
modifier = finalModifier
.semantics { role = Role.Button }
.clip(ButtonShape)
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }) {
onClick()
},
color = backgroundColor,
shadowElevation = elevation
) {
Row(
Modifier,
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
) {
content()
}
}
}
Usage:
CustomRoundedButton(modifier = Modifier.width(200.dp), content = {
CircularProgressIndicator(
modifier = Modifier.wrapContentHeight(),
color = MaterialTheme.colorScheme.onPrimary,
trackColor = MaterialTheme.colorScheme.onPrimary, // just to make it completely visible in preview
)
}) {
}
Output :
Update : If you look at the docs you can see
/** * The default min height applied for all buttons. Note that you can override it by applying * Modifier.heightIn directly on the button composable. */ val MinHeight = 40.dp
So you can provide heightIn as well instead of custom button. Below is the code using heightIn. Its the same as yours there is not need to provide this Modifier.align(Alignment.CenterVertically
for CircularProgressIndicator
@Preview
@Composable
fun ButtonSandbox() {
Button(
enabled = false,
modifier = Modifier.fillMaxWidth()
.heightIn(48.dp),
onClick = {},
colors = ButtonDefaults.buttonColors(
disabledContainerColor = MaterialTheme.colorScheme.primary,
disabledContentColor = MaterialTheme.colorScheme.onPrimary
)
) {
CircularProgressIndicator(
color = MaterialTheme.colorScheme.onPrimary,
trackColor = MaterialTheme.colorScheme.onPrimary,
)
}
}