I think I need to graduate to a new kind of animat...
# compose
t
I think I need to graduate to a new kind of animation? I'm trying to put together an effect that looks something like iOS's in placed table editing. When in "edit mode" selection controls slide in on one side of each row. I did this using a simple AnimatedVisibility on my selection button and tweaking the enter/exit parameters. The button itself looks nice, but the other elements of my row don't also animate their placement. They just snap update at the end? How should I go about getting this "selection button slides in AND the other widgets bunch up a little to make room for" effect as I go in and out of the "edit mode" ?
c
Do you want the other elements to move though? It seems the important part in the row is still the name in the list, and the timestamp is less useful? So you could use AnimatedContent to swap between the timestamp and the selection control in edit mode?
t
No, the dates are useful too. We get a lot of "similiar named" connections, where the datetime becomes an important discriminator.
d
What's the enter/exit params for your
AnimatedVisibility
? Combining
shrink
/`expand` with
slide
might get you close to what you want.
t
The AnimatedVisibility stuff works just fine. I slide them in from right to left, out from left to right. The problem is that the placement of the other elements in the row do not animate their movement. They suddenly just snap at the end of the animation. So maybe a better worded question is "how do animate the layout of all of the components in my row as the trailing element shifts in and out of visibility?"
Copy code
Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(vertical = 10.dp),
        verticalAlignment = Alignment.CenterVertically
    ) {
        Icon( ... )
        Spacer(modifier = Modifier.width(4.dp))
        Text(text = keyInfo.name, modifier = Modifier.weight(1.0f), maxLines = 1)
        Spacer(modifier = Modifier.width(10.dp))
        SettingsLabel(text = keyInfo.created.shortPrinted())
        AnimatedVisibility(visible = allowSelection,
            enter = slideInHorizontally(initialOffsetX = { w -> w }),
            exit = slideOutHorizontally(targetOffsetX = { w -> w })
        ) {
            IconButton(
                onClick = onSelectionClick, modifier = Modifier
                    .padding(start = 16.dp)
                    .size(28.dp)
            ) {
                when (isSelected) {
                    true -> Icon(
                        Icons.Outlined.CheckCircle, "${keyInfo.name} selected for edit"
                    )
                    false -> Icon(
                        Icons.Default.RadioButtonUnchecked, "${keyInfo.name} not selected for edit"
                    )
                }
            }
        }
    }
}
d
We don't currently have a simple way to animate the position of the sibling of the disappearing node. But you could try:
Copy code
AnimatedVisibility(visible = allowSelection,
enter = slideInHorizontally(...) + expandHorizontally(), exit = slideOutHorizontally(..) + shrinkHorizontally() )
The expand and shrink would create a size animation, which will influence the placement of sibling and result in a placement animation.
t
I thought maybe if I used an AnimatedContent around the Row... but it just flashes the whole Row (though I didn't set any of the parameters)
d
What does it look like when you add
expandHorizontally
to AnimatedVisibility? Can you share a video?
c
Considering you know the size of the icon button that animates in, you could use animateOffsetAsState with the width of the IconButton, on the SettingsLabel based on the visibility of the IconButton. If both use the same tween it should look coordinated. Not the cleanest, but it should work.
c
I think you could use AnimatedContent to change state render in one composable.
t
So I added expand/shrinkHorizontally as @Doris Liu and a reply to my SO post both suggested:
Copy code
enter = slideInHorizontally(initialOffsetX = { w -> w }) + expandHorizontally(expandFrom = Alignment.End),
exit = slideOutHorizontally(targetOffsetX = { w -> w }) + shrinkHorizontally(shrinkTowards = Alignment.End)
And now it looks beautiful. It's amazing how bad a janky/partial animation looks, and how convincing and comfortable a solid thorough animation looks. What I want to understand now is WHY this works?? Both transforms just seem like things that some black magic engine is using to move from an original layout box to a target layout box. But in one case (the slides), the Row composition seems indifferent to the intermediate process, adjusting siblings only at the end. But with the expand transitions, the Row composition suddenly becomes real time aware of this element and adjusts the other elements accordingly.
d
The expand*/shrink* transitions create a resize animation for the content as it enters or exits. That resize animation informs the parent (in this case the Row) of a different size each frame. In other words, the size change from the animation is observable by the parent layout (i.e. the Row). The Row then accommodates the changing size of the IconButton by doing a relayout of all its children. That's why you would see the other elements adjusting accordingly.
c
Always forget about expand and shrink!
t
But why doesn't the slide transition create a similar animation that informs its parent where things are at?