How can I prevent the "Hello Compose!" text from f...
# compose
n
How can I prevent the "Hello Compose!" text from flickering?
AnimatedVisibility
is not an option, this is a very simplified example, I need to switch between multiple UIs that may or may not have similar parts. Thanks!
Copy code
Column(
    modifier = modifier.padding(16.dp),
) {
    var shouldDisplayAction by remember { mutableStateOf(false) }

    Crossfade(
        modifier = Modifier.fillMaxWidth(),
        targetState = shouldDisplayAction,
        animationSpec = tween(easing = LinearEasing)
    ) { shouldDisplayAction ->
        Row {
            Text(
                modifier = Modifier.weight(1F),
                text = "Hello Compose!",
                fontSize = 30.sp,
            )

            if (shouldDisplayAction) {
                Text(
                    text = "MyAction",
                    fontSize = 16.sp,
                )
            }
        }
    }

    Spacer(modifier = Modifier.height(16.dp))

    Button(
        modifier = Modifier.align(Alignment.CenterHorizontally),
        onClick = { shouldDisplayAction = !shouldDisplayAction },
    ) {
        Text("Click me!")
    }
}
r
A cross-fade will always have this effect on parts that are not changing.
s
Run the crossfade only on the part which changes, or make it an AnimatedContent block and make the persistent part of the UI be a shared element
☝️ 2
Is what I would try myself
n
Why does it happen? Is it because there's "one frame lag" between the 2 animated content? In normal Android it worked fine with a LinearInterpolator (because "20%" of the white from fading out text would be paired with the "80%" of the fading in text). But I wonder why it's no longer the case in Compose 🤔
s
Why would it not happen? If you are fading there needs to be a point where the before and the after need to both not be at 100% opacity. Otherwise you'd not be cross-fading, but you'd just be swapping in-place
n
Run the crossfade only on the part which changes, or make it an AnimatedContent block and make the persistent part of the UI be a shared element
Unfortunately impossible, every part of the UI can appear or disappear in some cases, but most of the transitions are happening between those "identical overlapping parts" that makes a very bad UX 😞
s
Do a shared element then. Each item within this scope should be uniquely identifiable in some way I would hope?
n
I'm extra confused now. Why isn't alpha additive on superimposed pixels? I always assumed it was the case for some reason 🤔
Copy code
Column(
    modifier = modifier.padding(16.dp),
) {
    var alpha by remember { mutableFloatStateOf(0F) }

    Box(
        modifier = Modifier.fillMaxWidth(),
    ) {
        Text(
            modifier = Modifier.graphicsLayer {
                this.alpha = alpha
            },
            text = "Hello Compose!",
            fontSize = 30.sp,
        )

        Text(
            modifier = Modifier.graphicsLayer {
                this.alpha = 1 - alpha
            },
            text = "Hello Compose!",
            fontSize = 30.sp,
        )
    }

    Spacer(modifier = Modifier.height(16.dp))

    Slider(
        value = alpha,
        onValueChange = { alpha = it }
    )
    Text(text = alpha.toString())
}
s
It is, but you're assuming linear interpolation which is not the case by default.
You need to change the interpolation on the crossfade animation spec to be linear, instead of eased/spring (the default), and make sure the enter and exit have the same duration
n
I specified
animationSpec = tween(easing = LinearEasing)
for the
Crossfade
, isn't it enought? And here's the result with the code above, it looks like alpha isn't additive on those superimposed pixels for some reason
s
Yes, that is the easing. Linear is correct. There may be a delay in the enter transition that makes the two linear curves not compensate for each other
n
Yes that was my hypothesis too, but the example where I "crossfade" by myself with the slider value has the same issue so I'm super confused now
s
I'm out of ideas, sorry 😄
f
Just try to use a shared element transition
Wrap around by SharedTransitionLayout and use Modifier.sharedElement() on element you want to be static
r
Alpha is "additive" but not in the way you think it is.
Drawing 50% white over 50% white over black will give you a gray
A better way to think about alpha is that it's transmission
s
Ah yeah keep forgetting this 😄
r
50% white text will only let 50% of the light go through
So if you cover it with another 50% white text, you let only 25% of the bottom light go through, and after adding the 50% white you're drawing on top, you have something that's 75% white, not 100%
💡 1