1

I have a method 'activity' which hosts a 'screen' and since state hoisting is cool, I did just that but with a problem.

While the code works perfectly (incrementing the value) using Modifier.clickable, the same code does not properly work using detectTapGestures::onTap as can be observed through Log.d("ExampleScreen", "onClick Multi Count is $multiCount")

@Composable
fun ExampleActivity() {
    var multiCount by remember {
        mutableStateOf(0)
    }

    Log.d("ExampleActivity", "Multi Count is $multiCount") //this works either way

    ExampleScreen(
        multiCount = multiCount,
        incrementMultiCount = {
            multiCount = ++multiCount
        }
    )
}

  @Composable
    fun ExampleScreen(
        modifier: Modifier = Modifier,
        multiCount: Int,
        incrementMultiCount: () -> Unit
    ) {
            Log.d("ExampleScreen", "Example Multi Count is $multiCount") //this works either way
            Column(
                modifier = modifier
                    .fillMaxSize()
                    .padding()
            ) {
    
                Text(
                    modifier = Modifier
                        .size(100.dp, 50.dp)
                        .background(MaterialTheme.colorScheme.primaryContainer)
                        .clickable {//this works
                            incrementMultiCount()
                            Log.d("ExampleScreen", "onClick Multi Count is $multiCount")
                        }
           /*             .pointerInput(Unit) { //this does not work
                            detectTapGestures(
                                onTap = {
                                    incrementMultiCount()
                                    Log.d("ExampleScreen", "onClick Multi Count is $multiCount") //this stays 0
                                },
                                onLongPress = {
    
                                }
                            )
                        }*/, text = "Click to confirm"
                )
            }
    }

Partially, it works the same (incrementing works but not reading the value) as shown in logs defined in ExampleActivity and function body of ExampleScreen but doesn't in the onTapGesture.

If the remember value is directly in the ExampleScreen composable, onTap and clickable works perfectly but not what I wanted.

Finally, before suggesting I use what works, I wanted to use detectTapGestures because I really need the LongPress method for a secondary work.

Plus I will really appreciate an explanation since I thought both works the same.

Kinyo356
  • 135
  • 13

1 Answers1

4

You have to wrap your callback in rememberUpdatedState, something like this:

val onTap: () -> Unit = {
    incrementMultiCount()
    Log.d("ExampleScreen", "onClick Multi Count is $multiCount")
}
val updatedOnTap = rememberUpdatedState(onTap)

detectTapGestures(onTap = { updatedOnTap.value.invoke() })

This is what .clickable modifier does with its onClick argument as well, you can see that in its source code. Explanation can be found in rememberUpdatedState documentation:

rememberUpdatedState should be used when parameters or values computed during composition are referenced by a long-lived lambda or object expression. Recomposition will update the resulting State without recreating the long-lived lambda or object, allowing that object to persist without cancelling and resubscribing, or relaunching a long-lived operation that may be expensive or prohibitive to recreate and restart.

By the way, there is also Modifier.combinedClickable that can be used for long click detection.

Jan Bína
  • 3,447
  • 14
  • 16
  • I appreciate the explaination as well the other alternative you provided. They both worked. I will read more about it. – Kinyo356 Jan 04 '23 at 15:06