![](https://developer.android.com/static/images/training/constraint-layout/foldable_resized_2.gif)

In the[`ConstraintLayout`](https://developer.android.com/reference/androidx/constraintlayout/widget/ConstraintLayout)2.1 release, several features were added to help manage[foldable devices](https://developer.android.com/guide/topics/ui/foldables), including[`SharedValues`](https://developer.android.com/reference/androidx/constraintlayout/widget/SharedValues),[`ReactiveGuide`](https://github.com/androidx/constraintlayout/blob/d89c45dbb74bf19ad4834198a04af306696357bc/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ReactiveGuide.java), and enhanced support for animation with[`MotionLayout`](https://developer.android.com/reference/androidx/constraintlayout/motion/widget/MotionLayout).

## Shared Values

We added a new mechanism to inject runtime values in`ConstraintLayout`-- this is intended to be used for system-wide values, as all instances of`ConstraintLayout`are able to access the value.

In the context of foldable devices, we can use this mechanism to inject the position of the fold at runtime:  

### Kotlin

```kotlin
ConstraintLayout.getSharedValues().fireNewValue(R.id.fold, fold)
```

### Java

```java
ConstraintLayout.getSharedValues().fireNewValue(R.id.fold, fold);
```

In a custom helper, you can access the shared values by adding a listener for any changes:  

### Kotlin

```kotlin
val sharedValues: SharedValues = ConstraintLayout.getSharedValues()
sharedValues.addListener(mAttributeId, this)
```

### Java

```java
SharedValues sharedValues = ConstraintLayout.getSharedValues();
sharedValues.addListener(mAttributeId, this);
```

You can look at the[FoldableExperiments example](https://github.com/androidx/constraintlayout/blob/main/projects/FoldableExperiments/app/src/main/java/com/example/experiments/MainActivity.kt)to see how we capture the position of the fold using the[Jetpack WindowManager](https://developer.android.com/jetpack/androidx/releases/window)library and inject the position into`ConstraintLayout`.  

### Kotlin

```kotlin
inner class StateContainer : Consumer<WindowLayoutInfo> {

    override fun accept(newLayoutInfo: WindowLayoutInfo) {

        // Add views that represent display features
        for (displayFeature in newLayoutInfo.displayFeatures) {
            val foldFeature = displayFeature as? FoldingFeature
            if (foldFeature != null) {
                if (foldFeature.isSeparating &&
                    foldFeature.orientation == FoldingFeature.Orientation.HORIZONTAL
                ) {
                    // The foldable device is in tabletop mode
                    val fold = foldPosition(motionLayout, foldFeature)
                    ConstraintLayout.getSharedValues().fireNewValue(R.id.fold, fold)
                } else {
                    ConstraintLayout.getSharedValues().fireNewValue(R.id.fold, 0);
                }
            }
        }
    }
}
```

### Java

```java
class StateContainer implements Consumer<WindowLayoutInfo> {

    @Override
    public void accept(WindowLayoutInfo newLayoutInfo) {

        // Add views that represent display features
        for (DisplayFeature displayFeature : newLayoutInfo.getDisplayFeatures()) {
            if (displayFeature instanceof FoldingFeature) {
                FoldingFeature foldFeature = (FoldingFeature)displayFeature;
                if (foldFeature.isSeparating() &&
                    foldFeature.getOrientation() == FoldingFeature.Orientation.HORIZONTAL
                ) {
                    // The foldable device is in tabletop mode
                    int fold = foldPosition(motionLayout, foldFeature);
                    ConstraintLayout.getSharedValues().fireNewValue(R.id.fold, fold);
                } else {
                    ConstraintLayout.getSharedValues().fireNewValue(R.id.fold, 0);
                }
            }
        }
    }
}
```

`fireNewValue()`takes an ID representing the value as the first parameter and the value to inject as the second parameter.

## `ReactiveGuide`

![](https://developer.android.com/static/images/training/constraint-layout/fold-2.png)

One way to take advantage of a`SharedValue`in a layout, without having to write any code, is to use the[`ReactiveGuide`](https://github.com/androidx/constraintlayout/blob/main/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ReactiveGuide.java)helper. This will position a horizontal or vertical guideline according to the linked`SharedValue`.  

        <androidx.constraintlayout.widget.ReactiveGuide
            android:id="@+id/fold"
            app:reactiveGuide_valueId="@id/fold"
            android:orientation="horizontal" />

It can then be used as a you would with a normal guideline.

## `MotionLayout`for foldables

We added several features in`MotionLayout`in 2.1 that helps morphing state -- something particularly useful for foldables, as we typically have to handle animating between the different possible layouts.

There are two approaches available for foldables:

- At runtime, update your current layout (`ConstraintSet`) to show or hide the fold.
- Use a separate`ConstraintSet`for each of the foldable states you want to support (`closed`,`folded`, or`fully open`).

### Animating a`ConstraintSet`

The function`updateStateAnimate()`in`MotionLayout`was added in the 2.1 release:  

### Kotlin

```kotlin
fun updateStateAnimate(stateId: Int, set: ConstraintSet, duration: Int)
```

### Java

```java
void updateStateAnimate(int stateId, ConstraintSet set, int duration);
```

This function will automatically animate the changes when updating a given`ConstraintSet`instead of doing an immediate update (which you can do with`updateState(stateId, constraintset)`). This allows you to update your UI on the fly, depending on changes, such as which foldable state you are in.

### `ReactiveGuide`inside a`MotionLayout`

`ReactiveGuide`also supports two useful attributes when used inside a`MotionLayout`:

- `app:reactiveGuide_animateChange="true|false"`

- `app:reactiveGuide_applyToAllConstraintSets="true|false"`

The first one will modify the current`ConstraintSet`and animate the change automatically. The second one will apply the new value of the`ReactiveGuide`position to all`ConstraintSet`s in the`MotionLayout`. A typical approach for foldables would be to use a`ReactiveGuide`representing the fold position, setting up your layout elements relative to the`ReactiveGuide`.

### Using multiple`ConstraintSet`s to represent foldable state

Instead of updating the current`MotionLayout`state, another way to architect your UI to support foldables is to create specific separate states (including`closed`,`folded`, and`fully open`).

![](https://developer.android.com/static/images/training/constraint-layout/fold-1.png)

In this scenario, you might still want to use a`ReactiveGuide`to represent the fold, but you would have a lot more control (compared to the automated animation when updating the current`ConstraintSet`) on how each state would transition into another.

With this approach, in your`DeviceState`listener, you would simply direct the`MotionLayout`to transition to specific states through the[`MotionLayout.transitionToState(stateId)`](https://developer.android.com/reference/androidx/constraintlayout/motion/widget/MotionLayout#transitionToState(int))method.