Consider the following code:
val saveInProgress = false // in reality might come from a ViewModel
Button(onClick = {}) {
val text = if(saveInProgress) "Save" else "..."
Text(text)
}
When saveInProgress
is set to true, the Button
text gets shorter and the Button
resizes. I want the Button
to keep it's original size though (without setting a fixed size). How can I do that?
In Jetpack Version 1.4.x rememberTextMeasurer is introduced to measure text. By measuring your text you can get width even before laying outt anything in a Composable as
val text = "Initial Text Size"
val density = LocalDensity.current
val textMeasurer: TextMeasurer = rememberTextMeasurer()
val style: TextStyle = MaterialTheme.typography.button
val initialWidthDp: Dp = remember(text) {
with(density) {
textMeasurer.measure(
text = text,
style= style,
).size.width.toDp()
}
}
Style of button is required to measure text correctly.
Full sample
@Preview
@Composable
private fun TextWidthSample() {
val text = "Initial Text Size"
val density = LocalDensity.current
val textMeasurer: TextMeasurer = rememberTextMeasurer()
val style: TextStyle = MaterialTheme.typography.button
val initialWidthDp: Dp = remember(text) {
with(density) {
textMeasurer.measure(
text = text,
style = style,
).size.width.toDp()
}
}
var isLoading by remember {
mutableStateOf(false)
}
Column(
modifier = Modifier
.fillMaxSize()
.padding(20.dp)
) {
Button(
onClick = { /*TODO*/ }
) {
Text(
modifier = Modifier
.width(initialWidthDp),
text = if (isLoading) "Loading" else text,
textAlign = TextAlign.Center
)
}
Spacer(modifier = Modifier.height(20.dp))
Button(
onClick = { isLoading = isLoading.not() }
) {
Text("Loading: $isLoading")
}
}
}
If version is lower than 1.4.0 and don't want to use experimental api you can use Modifier.onSizeChanged with another recomposition or SubcomposeLayout without another recomposition. SubcomposeLayout can be used to get dimensions of Composable even before it's measured and laid out like adding progress button inside Button while loading.