https://kotlinlang.org logo
#compose
Title
# compose
d

dimsuz

03/06/2023, 5:18 PM
At times when writing animation logic I find myself needing to access previous state. For example something like:
Copy code
val transition = updateTransition(myState) { state ->
  when (state) {
    MyState.One -> if (previousState == MyState.Two) 1.0f else 0.0f
    MyState.Two -> ...
  }
Notice the previousState in
if
. To implement this I'd have to put
previousState
in a
mutableStateOf
or something like this. Which I do not like for some reason. Am I approaching this incorrectly and my thinking needs shifting? 🙂
c

curioustechizen

03/06/2023, 5:36 PM
AnimatedContent
allows you to customise the entry and exit animations depending on both the current state and target state. Perhaps you could look at the implementation to get some inspiration.
c

Chris Sinco [G]

03/07/2023, 5:56 AM
I’d suggest using AnimatedContent as mentioned or encapsulate your animation logic in a separate Composable and use Animatable. Setting the value of Animatable based on conditional logic feels fine for me at least.
A concrete animation example of when you are doing this would also help frame a better suggestion.
d

dimsuz

03/07/2023, 11:19 AM
Thank you. An more concrete example would be something like a custom navigation implementation, where I have:
Copy code
enum Screen { Screen1, Screen2 }

@Composable
fun Screen1() { ... }
@Composable
fun Screen2() { ... }

fun Container(currentScreen: Screen) {
  val offset1: IntOffset = updateTransition(currentScreen) { ... }
  val offset2: IntOffset = ...
  Box(modifier = Modifier.offset(offset1)) {
    Screen1()
  }
  Box(modifier = Modifier.offset(offset2)) {
    Screen2()
  }  
}
where "previousScreen" would be required to determine whether offsets should be animated "forward" or "backward" (push/pop). Transitioning from Screen1 -> Screen2 would mean "forward" and Screen2 -> Screen1 would mean backward.
c

curioustechizen

03/07/2023, 11:36 AM
I recommend going through this blog post - it covers some of what you are aiming for (although in the context of AnimatedContent, not navigation): https://medium.com/androiddevelopers/customizing-animatedcontent-in-jetpack-compose-629c67b45894
d

dimsuz

03/07/2023, 11:44 AM
Thanks! 👍
c

Chris Sinco [G]

03/07/2023, 3:56 PM
For custom navigation transitions, LookaheadLayout may be useful. @Doris Liu might have other ideas
d

Doris Liu

03/07/2023, 6:47 PM
If you intend to differentiate between push and pop, knowing the previous state may not be sufficient. Say you are transitioning from Screen 1 to Screen 2, it could be pushing screen 2 onto the stack, or popping screen 1 off the stack, unless there's an explicit pre-defined order of the screens in the stack. You should be able to access the previous state via
Transition.currentState
. Though based on your use case, it does seem like
AnimatedContent
is a better fit, as folks above have pointed out. 🙂
d

dimsuz

03/07/2023, 10:25 PM
In my case there's a way to determine the exact order of screens, it's explicitly encoded elsewhere, so I plan to use this. For now I have indeed been able to use
AnimatedContent
successfully! Thanks to you all! I will investigate
LookaheadLayout
a bit later too, curious to see how it works, and it would be great to have pre-measured target composables in some cases.
d

Doris Liu

03/07/2023, 11:58 PM
The lookahead pass will give you pre-calculated size & position for your layout, which can be used to create layout animations. The design intention is: if the layout could tell you where your icon is going to position itself, and how big your image is going to be after you move it into a different parent, you could apply an animation to the size & position to smooth over the layout change. 🙂
d

dimsuz

03/09/2023, 2:39 PM
Ah, so it's more about "shared element transitions"
d

Doris Liu

03/10/2023, 12:12 AM
It'll be useful for shared elements for sure. I also imagine it'll allow reactive size & position animations, kind of like
Modifier.animateContentSize
, but better. Knowing the future size would allow more possibilities in size animation. In addition to animating a clip bound like we do in
animateContentSize
, we could also animate the constraints, so that children in the layout would resize/relayout themselves to respond to the parent size change, like this:

https://developer.android.com/images/reference/androidx/compose/foundation/layout/row_arrangement_visualization.gif

😄
d

dimsuz

03/10/2023, 5:06 PM
Oh, wow, it seems I can use it for my "intrinsic content" bottom sheet. I have a "clone" of BottomSheetScaffold which has a special "mode" I call "intrinsic". Whenever it is in this mode, the sheet adapts its size to its contents. It does this with animation, and it also respects all the anchors (which are also customizable). This is currently based on a
SubcomoseLayout
+
Swipeable
and is full of subtle hacks and tricky logic, especially around size changes and measurements. It seems I have to try to implement this with
LookaheadLayout
too.
d

Doris Liu

03/10/2023, 6:20 PM
Do you have a video of this "intrinsic" mode? I'd love to see what it looks like if you don't mind sharing. It does sound like a candidate for lookahead. BTW, there's a set of improvements to
LookaheadLayout
APIs that landed in 1.5.0-alpha01. Let me know if/when you have a chance to try them out. 🙂
d

dimsuz

03/10/2023, 6:34 PM
yes, here is a little screen capture. you can see here that when I press + or - buttons, they cause sheet content to "relayout" and this causes sheet to recalculate its swipeable anchors along with recalculating the layout. Same happens when I press "collapse/expand" button in the top-right corner. Content actually changes height and sheet adapts. During all these changes sheet stays "swipeable", it can even have some fixed anchors additionally, they will work. Completely different composable layout could also be rendered and it will adjust "intrinsically" too. But it's not pretty internally 🙂 There are also visible artifacts during relayout but they are caused by other components up in the tree, will have to deal with them.
d

Doris Liu

03/10/2023, 6:47 PM
That looks pretty cool! 🤩 I noticed the items get added/removed without an animation, while the sheet animating to the new size. Curious if having the new/removed item in AnimatedVisibility would smooth over that change. Also, when collapsing the content, I see the bottom sheet showing a gap above the bottom of the screen. I believe that's an issue that was recently fixed/investigated. Which compose build are you using?
d

dimsuz

03/10/2023, 9:50 PM
Thank you! Actually this is not a compose bottom sheet at all, not the one from compose.material artefact. I've initially only used it as a reference for learning how to work with swipeable modifier + state and from that I've built my own, based on SubcomposeLayout + Swipeable. Those gaps were not there initially I believe those are some bugs introduced recently by our team members or me 🙂 Or maybe I didn't notice, not sure... This is currently running on Compose 1.3.1 iirc. I think there's AnimatedContent somewhere in there, but may be handled improperly or is intersecting with other animations, but yeah, also needs smoothing out.
d

Doris Liu

03/10/2023, 10:03 PM
I see. Good job on getting it working with a custom swipeable-based impl. 🙂
9 Views