Skip to main content

Advanced: Transitions key scoping

In all the samples we have presented so far, we followed very common pattern when defining transitions: assigning transition keys and creating transitions happened within one ComponentSpec. However, there are situations where this is not the case: transition key is assigned within ComponentSpec, while transitions themselves are defined in another.

If you try to do everything exactly how we did it up until now it will not work. It will simply look like there are no transitions defined.

@LayoutSpec
public class GlobalKeyParentComponentSpec {
@OnCreateLayout
static Component onCreateLayout(ComponentContext c) {
return GlobalKeyTransitionComponent.create(c).build();
}
@OnCreateTransition
static Transition onCreateTransition(ComponentContext c) {
return Transition.create(GlobalKeyTransitionComponentSpec.SQUARE_KEY)
.animate(AnimatedProperties.X);
}
}
@LayoutSpec
public class GlobalKeyTransitionComponentSpec {
public static final String SQUARE_KEY = "square";
@OnCreateLayout
static Component onCreateLayout(ComponentContext c, @State boolean toRight) {
return Column.create(c)
.child(
SolidColor.create(c)
.color(YELLOW)
.widthDip(80)
.heightDip(80)
.transitionKey(SQUARE_KEY))
.alignItems(toRight ? YogaAlign.FLEX_END : YogaAlign.FLEX_START)
.clickHandler(GlobalKeyTransitionComponent.onClickEvent(c))
.build();
}
@OnEvent(ClickEvent.class)
static void onClickEvent(ComponentContext c, @FromEvent View view) {
GlobalKeyTransitionComponent.onUpdateState(c);
}
@OnUpdateState
static void onUpdateState(StateValue<Boolean> toRight) {
toRight.set(!toRight.get());
}
}

The reason is that, by default, transition keys are only visible within the scope of the component spec where it is used. This “visibility” of transition keys is determined by TransitionKeyType. There are two options:

  • LOCAL - the default type, only visible within ComponentSpec where it is used
  • GLOBAL - makes a transition key visible through the whole ComponentTree. The drawback here is that the keys should be unique within the tree. Thus it usually takes an extra effort to use several component of the same type that assign GLOBAL transition keys within one tree and avoid transition keys collisions.
note

Litho throws an exception when a transition keys collision occurs, which may not be trivial to debug and resolve in case of GLOBAL transition keys. Thus we encourage you to use LOCAL transition keys and assign transition keys within the same Spec that defines transitions that target those keys.

There are two steps to take to change transition key type:

  1. Use Component.Builder#transitionKeyType() when assigning a key to a Component.
  2. When creating a Transition use a version of Transition.create() that takes TransitionKeyType argument along with the key itself.

Here is how we would fix the sample using TransitionKeyType.GLOBAL:

@LayoutSpec
public class GlobalKeyParentComponentSpec {
@OnCreateLayout
static Component onCreateLayout(ComponentContext c) {
return GlobalKeyTransitionComponent.create(c).build();
}
@OnCreateTransition
static Transition onCreateTransition(ComponentContext c) {
return Transition.create(
Transition.TransitionKeyType.GLOBAL, GlobalKeyTransitionComponentSpec.SQUARE_KEY)
.animate(AnimatedProperties.X);
}
}
@LayoutSpec
public class GlobalKeyTransitionComponentSpec {
public static final String SQUARE_KEY = "square";
@OnCreateLayout
static Component onCreateLayout(ComponentContext c, @State boolean toRight) {
return Column.create(c)
.child(
SolidColor.create(c)
.color(YELLOW)
.widthDip(80)
.heightDip(80)
.transitionKeyType(Transition.TransitionKeyType.GLOBAL)
.transitionKey(SQUARE_KEY))
.alignItems(toRight ? YogaAlign.FLEX_END : YogaAlign.FLEX_START)
.clickHandler(GlobalKeyTransitionComponent.onClickEvent(c))
.build();
}
@OnEvent(ClickEvent.class)
static void onClickEvent(ComponentContext c, @FromEvent View view) {
GlobalKeyTransitionComponent.onUpdateState(c);
}
@OnUpdateState
static void onUpdateState(StateValue<Boolean> toRight) {
toRight.set(!toRight.get());
}
}