1

I am familiar with the Transition API in Jetpack Compose that lets me easily animate between two states inside a Compose component.

But what would be the best way to animate between three different states?

Consider as an example a loading indicator for a component. The states could be NotLoading, Loading and HasLoaded. My thinking here would be to have a loading indicator for a component and transition between those states:

  • Transition for showing the loading indicator: NotLoading -> Loading
  • Transition for showing the data: Loading -> HasLoaded

I guess what kind of transition doesn't really matter but I was thinking first fade in loading indicator then fade out loading indicator and fade in content. But this is just an example; in reality I need to specify the transition parameters.

What would be the best way to achieve this with Jetpack Compose? Not sure if my state thinking here is the best approach for this either.

Johan Paul
  • 2,203
  • 2
  • 22
  • 38
  • You can use `Crossfade`, check out [this](https://stackoverflow.com/a/69160738/3585796) answer. – Phil Dukhov Apr 26 '22 at 04:15
  • Thanks for the suggestion! But unfortunately `Crossfade` does not work for me since in reality I need to be able to control the transition in finer detail and give parameters to how to transition between the states. AFAIK Crossfade does not allow me to specify the transition. – Johan Paul Apr 26 '22 at 04:17
  • Actually, I think using the Transition API would still work as it can transition between multiple states, although all examples are with just two. "Transition manages one or more animations as its children and runs them simultaneously between multiple states." – Johan Paul Apr 26 '22 at 04:39

1 Answers1

8

You can use the Transition API with more than 2 states - and define the individual properties of each component using animate*AsState APIs.

There is another option if you have completely different Composables, you can use the AnimatedContent APIs.

For example, the below sample uses an enum UiState, and a button to change between the states. The content is then wrapped inside the AnimatedContent() composable. By default, the initial content fades out and then the target content fades in (this behavior is called fade through).

@Composable
fun AnimatedContentStateExample() {
    Column {
        var state by remember { mutableStateOf(UiState.Loading) }
        Button(onClick = {
            state = when (state) {
                UiState.Loading -> UiState.Loaded
                UiState.Loaded -> UiState.Empty
                UiState.Empty -> UiState.Loading
            }
        }) {
            Text("Switch States")
        }
        AnimatedContent(
            targetState = state
        ) { targetState ->
            // Make sure to use `targetState`, not `state`.
            when (targetState) {
                UiState.Loading -> {
                    CircularProgressIndicator()
                }
                UiState.Loaded -> {
                    Box(
                        Modifier
                            .background(androidGreen)
                            .size(100.dp))
                    Text("Loaded")
                }
                UiState.Empty -> {
                    Box(
                        Modifier
                            .background(androidBlue)
                            .size(200.dp))
                    Text("Empty")
                }
            }
        }
    }
}

You can customize this animation behavior by specifying a ContentTransform object to the transitionSpec parameter. You can create ContentTransform by combining an EnterTransition with an ExitTransition using the with infix function. You can apply SizeTransform to the ContentTransform by attaching it with the using infix function.

More information about AnimatedContent can be found here: https://developer.android.com/jetpack/compose/animation#animatedcontent.

And samples of its usage here: https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/AnimatedContentSamples.kt

riggaroo
  • 2,584
  • 20
  • 34
  • Perfect! Thanks a lot! I think this is exactly what I need and it's nice to see that these kinds of things have been thought of in Compose. – Johan Paul Apr 26 '22 at 11:16
  • I wasn't able to use the syntax in the samples since it requires a ContentTransform as parameter. For example fadeIn() is an EnterTransition. So I had to wrap these transitions in an explicit ContentTransform() object. It doesn't look as nice as in the samples though. – Johan Paul May 03 '22 at 08:01