Search code examples
androidandroid-jetpack-composelottie

Tap Gesture Triggered on Swipe in Android


I'm working on an app that uses an animated character that has different animations triggered when the user taps on it and another animation when the user swipes on it. The issue is that the view registers the swipe gesture as a tap as well as shown below.

enter image description here

Here is the code handling the pointer input:

import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.tooling.preview.Preview
import com.airbnb.lottie.compose.*
import com.lumen.tomo.R
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

@Composable
fun ChatView() {
    val initialAnimation = R.raw.cat_idle  // Initial animation resource ID
    val tapAnimation = R.raw.cat_headshake          // Tap specific animation resource ID
    val swipeAnimation = R.raw.cat_heart      // Swipe specific animation resource ID

    var animationRes by remember { mutableIntStateOf(initialAnimation) }
    val idleAnimationComposition by rememberLottieComposition(LottieCompositionSpec.RawRes(initialAnimation))
    val tapAnimationComposition by rememberLottieComposition(LottieCompositionSpec.RawRes(tapAnimation))
    val swipeAnimationComposition by rememberLottieComposition(LottieCompositionSpec.RawRes(swipeAnimation))
    val coroutineScope = rememberCoroutineScope()  // Remember a CoroutineScope tied to the Composable's lifecycle

    LottieAnimation(
        composition = if (animationRes == initialAnimation) idleAnimationComposition else if (animationRes == tapAnimation) tapAnimationComposition else swipeAnimationComposition,  // Choose based on animationRe
        iterations = LottieConstants.IterateForever,
        modifier = Modifier
            .fillMaxSize()
            .pointerInput(Unit) {
                detectTapGestures(onTap = {
                    coroutineScope.launch {
                        animationRes = tapAnimation
                        delay(5000)
                        animationRes = initialAnimation
                    }
                })
                detectDragGestures { _, _ ->
                    coroutineScope.launch {
                        animationRes = swipeAnimation
                        delay(5000)
                        animationRes = initialAnimation
                    }
                }
            }
    )
}

Solution

  • Have a look at the official documentation:

    Internally, the detectTapGestures method blocks the coroutine, and the second detector is never reached. If you need to add more than one gesture listener to a composable, use separate pointerInput modifier instances instead.

    Please update your code and use two seperate pointerInput Modifiers:

    modifier = Modifier
        .fillMaxSize()
        .pointerInput(Unit) {
            detectTapGestures(onTap = {
                coroutineScope.launch {
                    animationRes = tapAnimation
                    delay(5000)
                    animationRes = initialAnimation
                }
            })
        }
        .pointerInput(Unit) {
            detectDragGestures { _, _ ->
                coroutineScope.launch {
                    animationRes = swipeAnimation
                    delay(5000)
                    animationRes = initialAnimation
                }
            }
        }