4

Updated - see in the bottom of the question.

We are trying to add content to a sticky topbar defined from each screen, and the solution works. But our custom modifier clickableWithScaleAnimation breaks when ever we add add a composable method to the stickyTopBar in ActualScreenComposable in the example below. It does just not scale or click, but if we add a simple .clickable {} after it, that will still work, so it is quite strange.

I suspect that the problem is related to the way of saving a composable method in a state, but it was working fine untill we spotted this exact problem. If we remove the stickyTopBar { ... } call it works, so it must be something with the was the state is set or handled. Any clues?

Scaffold(...) { 
    Column {
        NavigationGraph(...)
    }
}
    
    
fun NavigationGraph(...) {
    val stickyTopBarContent = remember { mutableStateOf<(@Composable () -> Unit)?>(null) }
    
    stickyTopBarContent.value?.let {
        it.invoke()
    }

    AnimatedNavHost {
        screenComposable(actuallScreenConfig, stickyTopBarContent)
        ...
    }
}

fun screenComposable(screenConfig, stickyTopBarContent, ...) {
    if (!screenConfig.supportsStickyTopBar) {
        stickyTopBarContent.value = null
    }
                
    ActualScreenComposable(stickyTopBar = { stickyTopBarContent.value = it }, ...)
}

fun ActualScreenComposable(stickyTopBar: (@Composable () -> Unit) -> Unit, ...) {
    stickyTopBar {
        StickyTopBar() // it even fails with no content in here
    }
    content
}

This is the modifier clickableWithScaleAnimation that does not work if the stickyTopBar { ... } is called.

fun content() {
    val coroutineScope = rememberCoroutineScope()
    Column(modifier.clickableWithScaleAnimation(
                coroutineScope = coroutineScope,
                scale = scale,
                scaleDownTo = 0.9f,
                animationDuration = 200,
                onClick = {
                    onSelected(...)
                }
            ))
         // .clickable { onSelected(...) } // this will work if added
            {
            ...
            }
        
}

fun Modifier.clickableWithScaleAnimation(
    interactionSource: MutableInteractionSource = MutableInteractionSource(),
    coroutineScope: CoroutineScope,
    scale: Animatable<Float, AnimationVector1D>,
    animationDuration: Int = 200,
    scaleDownTo: Float = 0.9f,
    onClick: () -> Unit
) = scale(scale.value)
    .clickable(interactionSource = interactionSource, indication = null) {
        coroutineScope.launch {
            scale.animateTo(scaleDownTo, animationSpec = tween(animationDuration))
            onClick()
            scale.animateTo(1f, animationSpec = tween(animationDuration))
        }
    }

We are using Compose 1.3.0

Update 1:

The problem is related to the clickable modifier. The clicked callback is never called. Our goal is just to remove the indication:

.clickable(interactionSource = interactionSource, indication = null) { ... } // fails
.clickable() { ... } // works

So there must be something in that specific modifier that somehow breaks - any clue what it could be?

Morten Holmgaard
  • 7,484
  • 8
  • 63
  • 85

1 Answers1

2

It is possible to solve like this with a CompositionLocalProvider to hide the indication and then use the clickable modifier without arguments, but it does not explain the cause of the problem.

fun content() {
    CompositionLocalProvider(LocalIndication provides NoIndication) {
    val coroutineScope = rememberCoroutineScope()
    Column(modifier.clickableWithScaleAnimation(
                coroutineScope = coroutineScope,
                scale = scale,
                scaleDownTo = 0.9f,
                animationDuration = 200,
                onClick = {
                    onSelected(...)
                }
            ))
   }     
}

object NoIndication : Indication {
    private object NoIndicationInstance : IndicationInstance {
        override fun ContentDrawScope.drawIndication() {
            drawContent()
        }
    }

    @Composable
    override fun rememberUpdatedInstance(interactionSource: InteractionSource): IndicationInstance {
        return NoIndicationInstance
    }
}
Morten Holmgaard
  • 7,484
  • 8
  • 63
  • 85