1

I'm making a custom Radio Button that wraps an animation when I go from selected to deselected and/or vice versa. I am using the AnimatedVisibility function of jetpack compose, however I am not getting the expected result.

Notice that in the gif above, despite the buttons changing state normally (selected to deselected and vice versa) the animation is not happening:

At the moment this code does not animate the buttons despite calling the animation function.

AnimatedRadioButton:

@ExperimentalAnimationApi
@Composable
fun AnimatedRadioButton(
    modifier: Modifier = Modifier,
    isSelected: Boolean,
) {
    if (isSelected)
        FadeAnimatedContainer {
            CircleOptionSelected(modifier = modifier)
        }
    else
        FadeAnimatedContainer {
            CircleOptionUnselected(modifier = modifier)
        }
}

@ExperimentalAnimationApi
@Composable
private fun FadeAnimatedContainer(
    content: @Composable AnimatedVisibilityScope.() -> Unit,
) = AnimatedVisibility(
    visible = true,
    enter = fadeIn(),
    exit = fadeOut(),
    content = content
)

CircleOptionSelected:

@Composable
fun CircleOptionSelected(
    modifier: Modifier = Modifier,
    @DimenRes ballSize: Int = R.dimen.ball_size // 24dp
) {
    val size = dimensionResource(id = ballSize)
    Box(
        modifier = modifier
            .size(size)
            .clip(CircleShape)
            .background(color = MyRed),
        contentAlignment = Alignment.Center
    ) {
        CircleOptionUnselected(ballSize = size / 2)
    }
}

CircleOptionUnselected:

private val UnselectedBallColor = Color(0xFFF2F2F2)

@Composable
fun CircleOptionUnselected(
    modifier: Modifier = Modifier,
    ballSize: Dp? = null
) {
    val size = ballSize ?: dimensionResource(id = R.dimen.ball_size)
    Surface(
        modifier = modifier.size(size),
        shape = CircleShape,
        color = UnselectedBallColor
    ) {

    }
}
Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
Pierre Vieira
  • 2,252
  • 4
  • 21
  • 41

1 Answers1

2

AnimatedVisibility works when visible is different between previous and current recompositions. In your code it's always true, so no animation should happen.

In your case AnimatedContent can be used. Note that using lambda parameter is critical with animation functions, like this one.

AnimatedContent(targetState = isSelected) { targetIsSelected ->
    if (targetIsSelected)
        CircleOptionSelected(modifier = modifier)
    else
        CircleOptionUnselected(modifier = modifier)
}
Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
  • This drives me crazy: I need to start my animation during the very first recomposition. I already spent 2 hours going through internet and trialling. I found out that it was possible before some Google update but Google cancelled that... But I cannot find any good alternative. Except of launching a background task which changes the state after a brief time... What a crazy solution... – Jiří Křivánek Sep 17 '22 at 15:51