I'm currently applying what I've learnt on compose with a little app, and I wanted to implement a button that has a loader replacing the text when the action is loading.
To do that, I want to implement a button that has either a Text composable or a CircularProgressIndicator, whether the data is loading or not, so the 2 composables are never in the button at the same time.
My problem is that with my implementation, only one of them exists at a time, considering I use a state to define if the button is loading or not.
Screenshot of the idle state:
Screenshot of the loading state (same scale):
Has anybody already encountered this kind of problem? I could put the CircularProgressIndicator at the end of the text but if I can, I would prefer to display one or the other.
Composable:
@Composable
fun ButtonWithLoader(
modifier: Modifier,
isLoading: Boolean,
title: String,
onClickAction: (() -> Unit)? = null
) {
Button(
modifier = modifier,
shape = RoundedCornerShape(50),
onClick = { onClickAction?.invoke() }
) {
if (isLoading) {
CircularProgressIndicator(
modifier = Modifier
.padding(MajorDimens.normal),
color = MajorColor.White
)
} else {
Text(
modifier = Modifier
.padding(MajorDimens.normal),
text = title,
style = MajorFonts.buttonText
)
}
}
}
CodePudding user response:
This is because by default minimum touch target size for CircularProgressIndicator is 48.dp. When it's in composition when isLoading true content height of Button is calculated as 48.dp
You can set a default height for your content so it won't change unless Text height is bigger than 48.dp.
@Composable
fun ButtonWithLoader(
modifier: Modifier,
isLoading: Boolean,
title: String,
onClickAction: (() -> Unit)? = null
) {
Button(
modifier = modifier,
shape = RoundedCornerShape(50),
onClick = { onClickAction?.invoke() }
) {
Box(
modifier = Modifier.height(48.dp),
contentAlignment = Alignment.Center
) {
if (isLoading) {
CircularProgressIndicator(
modifier = Modifier,
color = Color.White
)
} else {
Text(
modifier = Modifier,
text = title,
)
}
}
}
}
If Text can be bigger than 48.dp you can set minimum height so it will be set to height of bigger one and won't change
@Composable
fun ButtonWithLoader(
modifier: Modifier,
isLoading: Boolean,
title: String,
onClickAction: (() -> Unit)? = null
) {
Button(
modifier = modifier,
shape = RoundedCornerShape(50),
onClick = { onClickAction?.invoke() }
) {
Box(
modifier = Modifier.heightIn(min = 48.dp),
contentAlignment = Alignment.Center
) {
if (isLoading) {
CircularProgressIndicator(
modifier = Modifier,
color = Color.White
)
} else {
Text(
modifier = Modifier,
text = title,
fontSize = 40.sp
)
}
}
}
}