Current code
@Composable
fun TextDemo() {
var selectedIndex by remember {
mutableIntStateOf(0)
}
Row {
TabText(
text = "First",
isSelected = selectedIndex == 0,
onClick = {
selectedIndex = 0
},
)
TabText(
text = "Second",
isSelected = selectedIndex == 1,
onClick = {
selectedIndex = 1
},
)
}
}
@Composable
fun TabText(
text: String,
isSelected: Boolean,
onClick: () -> Unit,
) {
val tabTextColor by animateColorAsState(
targetValue = if (isSelected) {
Color.Red
} else {
Color.Black
},
animationSpec = tween(
easing = LinearEasing,
),
label = "tab_text_color",
)
Text(
modifier = Modifier
.padding(8.dp)
.clickable {
onClick()
},
text = text,
color = tabTextColor,
)
}
UI for reference
Two Text
in a Row
Layout Inspector recompositions
Question
How to reduce the recompositions when text color changes?
For properties like alpha
, transition
, etc, It is possible to avoid recompositions when animating using Modifier.graphicsLayer {}
The same code with alpha
animation instead of color is recomposed only once per selection change.
Code
@Composable
fun TabText(
text: String,
isSelected: Boolean,
onClick: () -> Unit,
) {
val alphaValue by animateFloatAsState(
targetValue = if (isSelected) {
1F
} else {
0.5F
},
animationSpec = tween(
easing = LinearEasing,
),
label = "tab_text_color",
)
Text(
modifier = Modifier
.graphicsLayer {
alpha = alphaValue
}
.padding(8.dp)
.clickable {
onClick()
},
text = text,
)
}
First of all when you log recomposition that reads a State it should better be done inside SideEffect
otherwise it's possible to get false positives, because logging itself also counts as a State read
.
To have one recomposition for text color change you can use Canvas
or any draw Modifier inside Tab
and call only draw phase while Color
changes using TextMeasurer
and drawText
function of DrawScope
.
Second option is to use BlendModes with Modifier.drawContent{} to change color with one recompostion as
@Preview
@Composable
private fun TextAnimationTest() {
var isSelected by remember {
mutableStateOf(false)
}
SideEffect {
println("Recomposing...")
}
val tabTextColor by animateColorAsState(
targetValue = if (isSelected) {
Color.Red
} else {
Color.Black
},
animationSpec = tween(
easing = LinearEasing,
),
label = "tab_text_color",
)
Column {
Button(
onClick = {
isSelected = isSelected.not()
}
) {
Text("Selected: $isSelected")
}
Text("Some Text", modifier = Modifier
.graphicsLayer {
compositingStrategy = CompositingStrategy.Offscreen
}
.drawWithContent {
drawContent()
drawRect(
color = tabTextColor,
blendMode = BlendMode.SrcIn
)
}
)
}
}