So the issue I'm facing is I have a button which can have dynamic text, button have a state isLoading
which when turned true, View is recomposed to show loader. Now Loader have its own width & height. How do I manage the loader to follow the same dimensions as of the text which was shown earlier. Right now when loader appears the button size grow large & after the loading is complete & isLoading
is false again, the button would come to its actually size with text inside it.
Edit 1: The approach suggested by @thracian is working fine, other than a weird issue where the button gets some predefined height (& so resulting in same value while calculating the view height), which makes the button looks taller than what I'm planning to implement. Attaching code for ref, how do I make this button follow wrapped content & at the same time get the same calculated value in view size as well.
@Composable
fun GreenCTA(
modifier: Modifier = Modifier, onClick: () -> Unit, title: String, isShowLoader: Boolean
) {
var buttonSize by remember {
mutableStateOf(DpSize.Zero)
}
val density = LocalDensity.current
Button(
modifier = modifier
.then(if (buttonSize != DpSize.Zero) modifier.size(buttonSize) else modifier)
.onSizeChanged { newSize ->
if (buttonSize == DpSize.Zero) {
buttonSize = with(density) {
newSize
.toSize()
.toDpSize()
}
}
},
onClick = { onClick.invoke() },
border = BorderStroke(1.dp, color = colorResource(id = R.color.pass_color_c2e6cd)),
colors = ButtonDefaults.buttonColors(backgroundColor = colorResource(id = R.color.pass_color_e3f6ec)),
shape = RoundedCornerShape(6.dp),
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 8.dp),
elevation = ButtonDefaults.elevation(0.dp),
) {
if (isShowLoader) BlueLoader(
modifier = Modifier
.fillMaxHeight()
.aspectRatio(3f)
)
else {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Image(
painter = painterResource(id = R.drawable.ic_add_tag),
contentDescription = "Button Logo"
)
Text(
text = title,
style = MaterialTheme.typography.body2.copy(lineHeight = 20.sp),
fontWeight = FontWeight.SemiBold,
)
}
}
}
}
If you wish to adjust size of the button based on Text which is composed initially you can use Modifier.onSizeChanged for Button dimensions while Text is displayed and then set these dimensions as size to button while Text is out of Composition such as
Preview
@Composable
private fun Test() {
var isLoading by remember { mutableStateOf(false) }
var buttonSize by remember { mutableStateOf(DpSize.Zero) }
val density = LocalDensity.current
OutlinedButton(
modifier = Modifier
.then(
if (buttonSize != DpSize.Zero) Modifier.size(buttonSize) else Modifier
)
.onSizeChanged { newSize ->
if (buttonSize == DpSize.Zero) {
buttonSize = with(density) {
newSize
.toSize()
.toDpSize()
}
}
},
onClick = { isLoading = isLoading.not() }) {
if (isLoading) {
CircularProgressIndicator(
modifier = Modifier
.fillMaxHeight()
.aspectRatio(1f)
)
} else {
Text(text = "Click me")
}
}
}
Also if you wish to adjust size of a Button to a Composable that is not composed yet, for instance loader size, you can use SubcomposeLayout to get dimensions beforehand as in this answer