Is there a better way to write this? ```val someBo...
# compose
d
Is there a better way to write this?
Copy code
val someBoolean = true // this is calculated
val translationSize = 100f // this is calculated

val animatedDelta by animateFloatAsState(if (someBoolean) 0.0f else 1.0f)
val value = if (someBoolean) {
    animatedDelta * -translationSize
} else {
    (1 - animatedDelta) * translationSize
}
a
Probably with the suspending animate APIs and produceState/LaunchedEffect to avoid recomposing just to finish computing the value
l
Also note that if you read states in lambdas like
graphicsLayer { /* here! */ }
, it won't trigger recomposition. I would write this:
Copy code
val animatedDelta by animateFloatAsState(...) // (do not read this in places other than inside graphicsLayer)
Box(Modifier.graphicsLayer { alpha /* or other properties */ = animatedDelta }) {
// maybe put some computations here
}
Maybe using other than
graphicsLayer
?
d
I was using the simple
graphicsLayer(translationY = ...)
instead of
graphicsLayer { ... }
. Didn't know the latter was better for recomposition.
Ha, I was trying to avoid using the suspend APIs to avoid refactoring but it looks like the way to go.
I'm currently doing something like this.
Copy code
val value: Float
if (someOtherBoolean) {
   // the code I posted above
} else {
   value = 0.0f
}
Modifier.graphicsLayer(translationY = value)
I had it this way so I could snap stuff back into position.
I refactored my code to use
AnimatableState
with the suspend APIs and
LaunchedEffect
, it works but it's a frame too slow. So I end up seeing jitter in the UI.
To explain what I'm doing. I have a
LazyColumn
and I'm animating a swap between two adjacent items. (This is for drag and drop/reordering) So first I do the swap in the dataset and when the items are composed in their new positions, I translate them to their old positions and then animate them into their new positions.
l
Maybe... did you run that with debug mode? Or it recomposes every frame it animates?
d
I'm on Compose for Desktop and there's no "debug mode" I think. I ran it without the debugger if that's what you're asking.
With my old code. • Before swap, everything is fine. • Swap happens in data set • When composing, I check for swap then set translation to old position • Then animate into the new position. With new
AnimatableState
• Before swap, everything is fine. • Swap happens in data set • When composing, the item is composed in new position • Eventually
LaunchedEffect
catches up and snaps the translation to the old position • Then animate into the new position
That's a better explanation of the issue I'm having with
AnimatableState
.
a
Can you post your code? Under the hood
animateFloatAsState
is using the same suspend APIs so there's not something intrinsically different there
d
I'm preparing a standalone project to push to GitHub. I'll post a link in a bit when I'm done tidying.
This is the bit of code https://github.com/Dominaezzz/compose-dnd/blob/master/src/main/kotlin/DragAndDrop.kt?ts=4#L203-L209 . (I might be doing some naughty things with state so brace yourself aha)
You'll see in
master
, the selected item follows smoothly but in the
animatable
branch, the selected item jitters when it's moved between other items.
a
avoid
CoroutineScope.launch
from composable functions, the
scope.launch { translation.snapTo(0f) }
calls in particular are going to be issues
d
Ah I forgot about that. I'm surprised that didn't cause more issues, tbh.
I've changed them to
LaunchedEffect
. I think I might need to use the transition API to make this do what I want.
a
Could be. There are also more animation APIs below Animatable that might be useful in some situations where you're jumping through too many hoops to use Animatable as a source of truth instead of bringing your own