createChildTransition
Functions summary
inline Transition<T> |
@ExperimentalTransitionApi
|
Cmn
|
Functions
Transition.createChildTransition
@ExperimentalTransitionApi
@Composable
inline fun <S : Any?, T : Any?> Transition<S>.createChildTransition(
label: String = "ChildTransition",
transformToChildState: @Composable (parentState) -> T
): Transition<T>
createChildTransition creates a child Transition based on the mapping between parent state to child state provided in transformToChildState. This serves the following purposes:
-
Hoist the child transition state into parent transition. Therefore the parent Transition will be aware of whether there's any on-going animation due to the same target state change. This will further allow sequential animation to be set up when all animations have finished.
-
Separation of concerns. The child transition can respresent a much more simplified state transition when, for example, mapping from an enum parent state to a Boolean visible state for passing further down the compose tree. The child composables hence can be designed around handling a more simple and a more relevant state change.
label is used to differentiate from other animations in the same transition in Android Studio.
import androidx.compose.animation.core.ExperimentalTransitionApi import androidx.compose.animation.core.Transition import androidx.compose.animation.core.animateDp import androidx.compose.animation.core.animateFloat import androidx.compose.animation.core.createChildTransition import androidx.compose.animation.core.updateTransition import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Button import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.scale import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp // enum class DialerState { DialerMinimized, NumberPad } @OptIn(ExperimentalTransitionApi::class) @Composable fun DialerButton(visibilityTransition: Transition<Boolean>, modifier: Modifier) { val scale by visibilityTransition.animateFloat { visible -> if (visible) 1f else 2f } Box(modifier.scale(scale).background(Color.Black)) { // Content goes here } } @Composable fun NumberPad(visibilityTransition: Transition<Boolean>) { // Create animations using the provided Transition for visibility change here... } @OptIn(ExperimentalTransitionApi::class) @Composable fun childTransitionSample() { var dialerState by remember { mutableStateOf(DialerState.NumberPad) } Box(Modifier.fillMaxSize()) { val parentTransition = updateTransition(dialerState) // Animate to different corner radius based on target state val cornerRadius by parentTransition.animateDp { if (it == DialerState.NumberPad) 0.dp else 20.dp } Box( Modifier.align(Alignment.BottomCenter) .widthIn(50.dp) .heightIn(50.dp) .clip(RoundedCornerShape(cornerRadius)) ) { NumberPad( // Creates a child transition that derives its target state from the parent // transition, and the mapping from parent state to child state. // This will allow: // 1) Parent transition to account for additional animations in the child // Transitions before it considers itself finished. This is useful when you // have a subsequent action after all animations triggered by a state change // have finished. // 2) Separation of concerns. This allows the child composable (i.e. // NumberPad) to only care about its own visibility, rather than knowing about // DialerState. visibilityTransition = parentTransition.createChildTransition { // This is the lambda that defines how the parent target state maps to // child target state. it == DialerState.NumberPad } // Note: If it's not important for the animations within the child composable to // be observable, it's perfectly valid to not hoist the animations through // a Transition object and instead use animate*AsState. ) DialerButton( visibilityTransition = parentTransition.createChildTransition { it == DialerState.DialerMinimized }, modifier = Modifier.matchParentSize(), ) } } }