Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Jetpack Compose (Composable) Change Image source with animation

I have a vector drawable set as source via the painter attribute to an Image. Now I want to change the source, but also animate the change. By animation I don`t mean morphing animation with the path data, rather I want to have simple FadeIn, FadeOut effects. So once the source is changed I want the to hide the previous and show the current drawable with fade animation.

enter image description here

Now I am doing a workaround, I am using 2 Images with the sources of the two different images, and using AnimatedVisibility to change the visibility of the images, to match the theme. Is there a standard way of changing the source with animation?

Here is the hack I use, which is very ugly in my opinion

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun AnimatedImage(modifier: Modifier, isLightTheme: Boolean, srcLight: Int = R.drawable.ic_sun, srcDark: Int = R.drawable.ic_moon) {

    val colors = LocalColors.current
    val (enter, exit) = remember {
        arrayListOf(
            fadeIn(animationSpec = tween(durationMillis = 1500)),
            fadeOut(animationSpec = tween(durationMillis = 500))
        )
    }

    AnimatedVisibility(
        visible = !isLightTheme,
        enter = enter as EnterTransition,
        exit = exit as ExitTransition
    ) {
        Image(
            painter = painterResource(id = srcDark), contentDescription = "",
            colorFilter = ColorFilter.tint(colors.secondsArrow),
            modifier = modifier
        )
    }

    AnimatedVisibility(
        visible = isLightTheme,
        enter = enter,
        exit = exit
    ) {
        Image(
            painter = painterResource(id = srcLight), contentDescription = "",
            colorFilter = ColorFilter.tint(colors.secondsArrow),
            modifier = modifier
        )
    }
}
like image 720
slaviboy Avatar asked Sep 03 '26 17:09

slaviboy


1 Answers

You can use basic Crossfade animation:

Crossfade(
    flag,
    animationSpec = tween(1000)
) { targetState ->
    Image(
        painterResource(if (targetState) R.drawable.ic_redo else R.drawable.ic_undo),
        contentDescription = null,
        modifier = Modifier.background(Color.Black)
    )
}

Or if you need a more complex one, you can use AnimatedContent - my example is equivalent to your double-AnimatedVisibility:

AnimatedContent(
    flag,
    transitionSpec = {
        fadeIn(animationSpec = tween(durationMillis = 1500)) with
                fadeOut(animationSpec = tween(durationMillis = 500))
    }
) { targetState ->
    Image(
        painterResource(if (targetState) R.drawable.ic_redo else R.drawable.ic_undo),
        contentDescription = null,
        modifier = Modifier.background(Color.Black)
    )
}

Note that in both cases it's required to use targetState passed in the content lambda, because this lambda is recomposed multiple times during transition.

You can find more info in Compose Animation documentation

like image 87
Philip Dukhov Avatar answered Sep 06 '26 07:09

Philip Dukhov