Chait
Chait

Reputation: 677

How to use animation listener for Lottie animation in Android Jetpack Compose?

In Android View System, We can use animation listener like below to get lottie animation callbacks.

 playView = LottieAnimationView(this)
 playView.addAnimatorListener(object : Animator.AnimatorListener {
        override fun onAnimationStart(animation: Animator?) {
        }

        override fun onAnimationEnd(animation: Animator?) {
           
        }

        override fun onAnimationCancel(animation: Animator?) {
        }

        override fun onAnimationRepeat(animation: Animator?) {
        }
    })

How we can add listener using Jetpack Compose? i added below code currently to play the lottie animation. I want to receive the callback after animation play completed.

@Composable
fun PlayView() {
    val animationSpec = remember { LottieAnimationSpec.RawRes(R.raw.play_anim) }
    val result: LottieCompositionResult by remember { mutableStateOf(LottieCompositionResult.Loading) }
    val context = LocalContext.current
    LottieAnimation(
        animationSpec,
        modifier = Modifier
            .fillMaxHeight()
            .fillMaxWidth()
    )

    if (result is LottieCompositionResult.Success) {
        //start the next flow
    }
}

Upvotes: 12

Views: 9218

Answers (4)

IVAN BOLTYSHEV
IVAN BOLTYSHEV

Reputation: 1

I use next solution

private enum class LottieAnimationState {
PENDING, STARTED, PLAYING, ENDED
}

@Composable
fun ObserveLottieAnimationState(
progress: Float,
onStarted: () -> Unit = {},
onPlaying: (Float) -> Unit = {},
onEnded: () -> Unit = {}
) {
var state by remember { mutableStateOf(LottieAnimationState.PENDING) }
var wasStarted by remember { mutableStateOf(false) }

LaunchedEffect(progress) {
    val newState = when {
        progress == 0f -> LottieAnimationState.PENDING
        progress > 0f && progress < 1f && !wasStarted -> {
            wasStarted = true
            LottieAnimationState.STARTED
        }

        progress > 0f && progress < 1f -> LottieAnimationState.PLAYING
        progress == 1f -> LottieAnimationState.ENDED
        else -> LottieAnimationState.PENDING
    }

    if (newState != state) {
        state = newState

        when (state) {
            LottieAnimationState.PENDING -> {}
            LottieAnimationState.STARTED -> onStarted()
            LottieAnimationState.PLAYING -> onPlaying(progress)
            LottieAnimationState.ENDED -> onEnded()
        }
    }
}
}

In UI

    val progress by animateLottieCompositionAsState(
    composition = composition,
    iterations = 1,
    isPlaying = true,
    restartOnPlay = false
)

ObserveLottieAnimationState(progress, onStarted = {
    Log.d("CheckLottieProgress", "onStarted")
}, onPlaying = {
    Log.d("CheckLottieProgress", "onPlaying")
}, onEnded = {
    Log.d("CheckLottieProgress", "onEnded")
})

My Logs:

D onStarted

D onPlaying

D onEnded

Upvotes: 0

Mark McClelland
Mark McClelland

Reputation: 5298

I was able to achieve this using the LottieAnimationState returned by animateLottieCompositionAsState(). In this case, I play the animation 3 times, then hide it, but you could put anything in the block where isAnimationVisible is set to false.

Note that I was using lottie-compose v6.4.0 here.

var isAnimationVisible by remember { mutableStateOf(true) }

if (isAnimationVisible) {
    val composition by rememberLottieComposition(LottieCompositionSpec.Asset("animation.json"))
    val animationState = animateLottieCompositionAsState(
        composition = composition,
        iterations = 3
    )

    LaunchedEffect(animationState.progress) {
        if (animationState.progress == 1f) {
            // Animation finished
            isAnimationVisible = false
        }
    }

    composition?.also {
        LottieAnimation(
            composition = it,
            progress = { animationState.progress },
        )
    }
}

Upvotes: 2

Inliner
Inliner

Reputation: 1659

You can use animatable isAtEnd/isPlaying/progress for that (6.4.0v)

    val composition by rememberLottieComposition(
        LottieCompositionSpec.RawRes(R.raw.your_lottie_file)
    )
    var shouldPlayAnimation by remember { mutableStateOf(false) }
    val animatable = rememberLottieAnimatable()

    LaunchedEffect(composition, shouldPlayAnimation) {
        if (composition == null || !shouldPlayAnimation) return@LaunchedEffect
        animatable.animate(composition, iteration = 1)
    }

    LottieAnimation(
        composition = composition,
        progress = { animatable.progress },
        modifier = Modifier
            .size(300.dp, 200.dp)
    )

    LaunchedEffect(animatable.isAtEnd) {
        if (animatable.isAtEnd) {
            shouldPlayAnimation = false
            animatable.resetToBeginning()
        }

Upvotes: 1

nglauber
nglauber

Reputation: 23984

Updated answer to version 1.0.0-rc01-1 of Lottie.

What you can do is:

val composition by rememberLottieComposition(
    LottieCompositionSpec.RawRes(R.raw.your_lottie_file)
)
val progress by animateLottieCompositionAsState(composition)

LottieAnimation(
    composition,
    modifier = Modifier
        .size(200.dp)
        .background(Color.Black),
) 

if (progress == 1.0f) {
    // Animation completes.
}

Upvotes: 28

Related Questions