Transition
public class Transition<S>
internal constructor(
private val transitionState: TransitionState<S>,
@get:RestrictTo(RestrictTo.Scope.LIBRARY) public val parentTransition: Transition<*>?,
public val label: String? = null,
)
Transition
manages all the child animations on a state level. Child animations can be created
in a declarative way using Transition.animateFloat
, Transition.animateValue
,
animateColor
etc. When the targetState
changes,
Transition
will automatically start or adjust course for all its child animations to animate to
the new target values defined for each animation.
After arriving at targetState
, Transition
will be triggered to run if any child animation
changes its target value (due to their dynamic target calculation logic, such as theme-dependent
values).
Secondary Constructors
@PublishedApi
internal constructor(
transitionState: TransitionState<S>,
label: String? = null,
) : this(transitionState, null, label)
internal constructor(
initialState: S,
label: String?,
) : this(MutableTransitionState(initialState), null, label)
@PublishedApi
internal constructor(
transitionState: MutableTransitionState<S>,
label: String? = null,
) : this(transitionState as TransitionState<S>, null, label)
Functions
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public fun setPlaytimeAfterInitialAndTargetStateEstablished(
initialState: S,
targetState: S,
playTimeNanos: Long,
)
This allows tools to set the transition (between initial and target state) to a specific
playTimeNanos
.
Note: This function is intended for tooling use only.
Caveat: Once initialState
or targetState
changes, it needs to take a whole
composition pass for all the animations and child transitions to recompose with the new
initialState
and targetState
. Subsequently all the animations will be updated to the
given play time.
Caveat: This function puts Transition
in a manual playtime setting mode. From then on
the Transition
will not resume normal animation runs.
Code Examples
GestureAnimationSample
@Composable
fun GestureAnimationSample() {
// enum class ComponentState { Pressed, Released }
var useRed by remember { mutableStateOf(false) }
var toState by remember { mutableStateOf(ComponentState.Released) }
val modifier =
Modifier.pointerInput(Unit) {
detectTapGestures(
onPress = {
toState = ComponentState.Pressed
tryAwaitRelease()
toState = ComponentState.Released
}
)
}
// Defines a transition of `ComponentState`, and updates the transition when the provided
// [targetState] changes. The transition will run all of the child animations towards the new
// [targetState] in response to the [targetState] change.
val transition: Transition<ComponentState> = updateTransition(targetState = toState)
// Defines a float animation as a child animation the transition. The current animation value
// can be read from the returned State<Float>.
val scale: Float by
transition.animateFloat(
// Defines a transition spec that uses the same low-stiffness spring for *all*
// transitions of this float, no matter what the target is.
transitionSpec = { spring(stiffness = 50f) }
) { state ->
// This code block declares a mapping from state to value.
if (state == ComponentState.Pressed) 3f else 1f
}
// Defines a color animation as a child animation of the transition.
val color: Color by
transition.animateColor(
transitionSpec = {
when {
ComponentState.Pressed isTransitioningTo ComponentState.Released ->
// Uses spring for the transition going from pressed to released
spring(stiffness = 50f)
else ->
// Uses tween for all the other transitions. (In this case there is
// only one other transition. i.e. released -> pressed.)
tween(durationMillis = 500)
}
}
) { state ->
when (state) {
// Similar to the float animation, we need to declare the target values
// for each state. In this code block we can access theme colors.
ComponentState.Pressed -> MaterialTheme.colors.primary
// We can also have the target value depend on other mutableStates,
// such as `useRed` here. Whenever the target value changes, transition
// will automatically animate to the new value even if it has already
// arrived at its target state.
ComponentState.Released -> if (useRed) Color.Red else MaterialTheme.colors.secondary
}
}
Column {
Button(
modifier = Modifier.padding(10.dp).align(Alignment.CenterHorizontally),
onClick = { useRed = !useRed },
) {
Text("Change Color")
}
Box(
modifier
.fillMaxSize()
.wrapContentSize(Alignment.Center)
.size((100 * scale).dp)
.background(color)
)
}
}