I have a scenario I'm not sure how to progress wit...
# compose
a
I have a scenario I'm not sure how to progress with. I have a composable that either animates on every "tick" or is scrollable by the user. I'm using the
translationY
value for both the scrolling and the tick. Right now, when I toggle between scrolling mode or ticking animation mode the composable kind of swaps between the two different translationY values. I'd like to remember the old translationY and increment from there but I'm not sure how to make that happen...I've added the relevant code to this thread.
Copy code
fun VerticalDigits(focusedDigit: Int, scrollable: Boolean) {
    val translationAnimation: Float by animateFloatAsState(
        targetValue = verticalDigitTranslation(focusedDigit, heightPixels)
    )

    var scrollableTranslation by remember { mutableStateOf(translationAnimation) }
    val heightModifier = Modifier.height(height)

    val translation = if (scrollable) scrollableTranslation else translationAnimation

    return Column(
        modifier = Modifier
            .graphicsLayer(translationY = translationY)
            .scrollable(
                orientation = Orientation.Vertical, state = rememberScrollableState(
                    consumeScrollDelta = { delta ->
                        scrollableTranslation += delta
                        delta
                    })
            )
    ) { ... }
The problem here is that I'm swapping between the "scrollable" translation and the like "animation" translation, but I'm not sure how to get one to start from the other if that makes sense.
f
Well, one is derived from focusedDigit and one has it's own state. Now you have two sources holding the same type of data. I think the solution is to derive both translationAnimation and scrollableAnimation from one source. That means that both values will be updated at all times and you just have to switch between them.
a
I guess I'm not sure how to do that though. Are you suggesting pulling the like translation state up to a parent composable?
f
Yeah, hold the state in parent composable and let
VerticalDigits
display it and return changes via function argument '`(delta: Float) -> Unit`' I don't know how
focusedDigit
is calculated or what
verticalDigitTranslation
does but here is an example:
Copy code
fun Root() {
    var translation by remember { mutableState(0f) } // change on scroll
    val focusedDigit = calculateFocusedDigit(translation) 

    VerticalDigits(focusedDigit, scrollable) { translation += it }
}


fun VerticalDigits(focusedDigit: Int, scrollable: Boolean, onTranslationChange: (Float) -> Unit) {
    val translationAnimation by animateFloatAsState(verticalDigitTranslation(focusedDigit, heightPixels))

    // The rest of the function
}
(I haven't tested it)
I hope it makes sense (and that I'm not sharing bad practices - I am still learning Compose myself) Feel free to correct me
a
Hmmm I'll play around with that. Thanks!
d
The two sources of truth (i.e. gesture and animation) need to streamlined into one. I recommend using
Animatable
for this type of use cases: you can animate using
Animatable.animateTo
. When scrolling happens, call
Animatable.snapTo(...)
to stop the animation and update the scroll value. I'd do something like this: (not tested)
Copy code
@Composable
fun VerticalDigits(focusedDigit: Int, scrollable: Boolean) {
    val translationAnimation = remember {
        Animatable(verticalDigitTranslation(focusedDigit, heightPixels))
    }
    LaunchedEffect(focusedDigit, heightPixels) {
        translationAnimation.animateTo(
            targetValue = verticalDigitTranslation(focusedDigit, heightPixels)
        )
    }
    val scope = rememberCoroutineScope()
        ...
    Modifier
        .scrollable(
            orientation = Orientation.Vertical, state = rememberScrollableState(
                consumeScrollDelta = { delta ->
                    scope.launch {
                        translationAnimation.snapTo(translationAnimation.value + delta)
                    }
                    delta
                })
        ).graphicsLayer {
            translationY = translationAnimation.value
        }
}
The gesture animation section of the tutorial might be helpful: https://developer.android.com/jetpack/compose/animation#gesture-and-animation
a
Thank you, I'll try this out!