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?
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
}
}