Skip to main content

Types of Transitions

Litho supports animated transitions between two consecutive states of the UI (LayoutStates), which, on this page, are called:

  • Before - the state of the UI before the updates, prior to the start of the transition.
  • After - the state of the UI after the update, after the transition has ended.

When defining transitions, it's necessary to indicate to the framework what Component/AnimatedProperty pairs are to be animated. Based on the presence of the Component in before and after states, three types of transitions can be defined: Change, Appear, and Disappear.

Change transitions​

A transition where the target Component is present in both before and after LayoutStates is called a Change transition.

The SimpleAllLayoutTransitionComponent featured in the Animation Basics page features a Change transition, since the 'square' Component is always present in the layout but changes its position (X property). The 'square' is illustrated in the following animation.

How the Change type functions internally​

Imagine a scenario in which layout A (the before state) is mounted (rendered on the screen) and there exists a blueprint of layout B (the after state), which the framework is ready to render. Both of these layouts have ComponentC present, and there is a transition defined for a AnimatedProperty.P of this component.

The framework needs to take the following steps in order to run change animations:

  1. Pick the current value of the AnimatedProperty.P from the mounted UI element (a View or 'Drawable') that represents ComponentC on the screen. This is the start value for the animation.
  2. Pick the animation end value of AnimatedProperty.P from the blueprint of layout B.
  3. When the layout B is mounted to the screen, instead of applying the new value of 'P' immediately, keep the previous value in place, subscribe to the Choreographer, then start changing the value of P for the new mounted UI element on every frame until it reaches the end value.
note

ComponentC is present in both trees.

Before:

Column(alignItems = YogaAlign.FLEX_START) { // AnimatedProperty.X aligned to start
child(
SolidColor( // <- ComponentC
color = YELLOW,
style = Style.width(80.dp).height(80.dp).transitionKey(context, SQUARE_KEY)))
}

After:

Column(alignItems = YogaAlign.FLEX_END) {   // AnimatedProperty.X aligned to end
child(
SolidColor( // <- ComponentC
color = YELLOW,
style = Style.width(80.dp).height(80.dp).transitionKey(context, SQUARE_KEY)))
}

Appear transitions​

A transition where the target Component appears on screen is called an Appear transition. The target Component is not present in the before LayoutState and is present in the after LayoutState.

In order to run transitions, you need the start and end values of the AnimatedProperty. The framework provides the end value and the user provides the start value. The start value can be specified by using the .appearFrom() method when building a Transition, as shown in the following LayoutSpec:

@LayoutSpec
public class AppearTransitionComponentSpec {

private static final String SQUARE_KEY = "square";

@OnCreateLayout
static Component onCreateLayout(ComponentContext c, @State boolean shown) {
Component child;
if (shown) {
child =
SolidColor.create(c)
.color(YELLOW)
.widthDip(80)
.heightDip(80)
.transitionKey(SQUARE_KEY)
.build();
} else {
child = null;
}

return Column.create(c)
.heightPercent(100)
.child(child)
.clickHandler(AppearTransitionComponent.onClickEvent(c))
.alignItems(YogaAlign.FLEX_END)
.build();
}

@OnCreateTransition
static Transition onCreateTransition(ComponentContext c) {
return Transition.create(SQUARE_KEY)
.animate(AnimatedProperties.X)
.appearFrom(0f)
.animate(AnimatedProperties.ALPHA)
.appearFrom(0f);
}

@OnEvent(ClickEvent.class)
static void onClickEvent(ComponentContext c, @FromEvent View view) {
AppearTransitionComponent.onUpdateState(c);
}

@OnUpdateState
static void onUpdateState(StateValue<Boolean> shown) {
shown.set(!shown.get());
}
}

The following animation shows the Appear transition in action.

How the Appear type functions internally​

Using the same scenario described for the Change type:

  1. Pick the appearFrom value of AnimatedProperty.P from the Transition object. This is the start value for the animation.
  2. Pick the animation end value of AnimatedProperty.P from the blueprint of layout B.
  3. When layout B is mounted to the screen, instead of applying the end value from that layout to the mounted item immediately, set the user-provided appearFrom value and drive that to the end value from the layout.

Before:

val child = null

Column(alignItems = YogaAlign.FLEX_END) {
child(child) // ComponentC is null here
}

After:

val child =
SolidColor(
color = YELLOW,
style = Style.width(80.dp).height(80.dp).transitionKey(context, SQUARE_KEY))

Column(alignItems = YogaAlign.FLEX_END) {
child(child) // ComponentC is present and will be animated.
}

Disappear transitions​

The Disappear transition type is the opposite of the Appear transition type.

A transition where the target Component disappears from the screen is called a Disappear transition. The target Component is present in the before LayoutState and is not present in the after LayoutState.

In order to configure a Disappear transition, you need to provide an end value for the property that is disappearing. The end value can be specified by using the .disappearTo() method when building a Transition, as shown in the following code:

useTransition(
Transition.create(SQUARE_KEY)
.animate(AnimatedProperties.X)
.appearFrom(0f)
.animate(AnimatedProperties.ALPHA)
.appearFrom(0f)
.disappearTo(0f)
.animate(AnimatedProperties.SCALE)
.disappearTo(0.5f))
}

The following animation shows the Disappear transition in action.

How the Disappear type functions internally​

  1. Pick the start value from the mounted UI element that represents the Component.
  2. Retrieve the end value from the user-provided disappearTo value of the transition definition.
  3. Render the after layout, but instead of removing the UI element from the screen immediately, drive the value of AnimatedProperty to the end value then remove the UI element.
note

The disappearing component no longer exists in the Component Tree, but must still exist in the view hierarchy in order to be rendered for the duration of the animation. To achieve this, the component that's being animated out is moved from its current container in the before state to the root component of the after state, and rendered above all other content. Once the animation ends, the disappearing component (and its associated View / Drawable) is fully removed.

Before:

val child =
SolidColor(
color = YELLOW,
style = Style.width(80.dp).height(80.dp).transitionKey(context, SQUARE_KEY))

Column(alignItems = YogaAlign.FLEX_END) {
child(child) // ComponentC is present and will be animated.
}

After:

val child = null

Column(alignItems = YogaAlign.FLEX_END) {
child(child) // ComponentC is null here
}
info

During a transition, a disappearing Component is not part of the layout tree. Instead, it is drawn on top of all the remaining components. This may cause visual glitches in the component's z-order.