Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to reduce recompositions when animating text color?

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

UI Screenshot

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Layout Inspector recompositions

Screenshot of Layout inspector

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.

Layout inspector screenshot

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,
    )
}

>Solution :

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
              )
          }
      )
  }
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading