# Animations with Lookahead in Jetpack Compose

The **LookaheadScope** (replaced by the previous `LookaheadLayout`) is a new experimental API in Jetpack Compose that allows for predictive animations based on the future state of the layout by pre-calculating size and position. It supports a lookahead pass of measure and layout before the actual measure/layout. Thus the latter can make use of the values pre-calculated during the previous to update the node on every frame.

**LookaheadLayout** was first introduced in **Version 1.3.0-alpha01** ([see commit](https://android-review.googlesource.com/c/platform/frameworks/support/+/1961800)). Since then the API has gone through many changes. Check for some of the important changes listed at the end of this article.

As mentioned by **Jaewoong Eum** in this [tweet](https://twitter.com/github_skydoves/status/1531115821161799680): “This allows us to look ahead and calculate a new layout while allowing the actual measurement & placement of every frame to be different than the pre-calculation.” Also, **Doris Liu** in this [tweet](https://twitter.com/doris4lt/status/1531365874828865536) shared an amazing example of lookahead with movable content, which made it possible to move UI between a single-column and a double-column layout without losing their animation states.

In this article, I will try to explain the various functions and interfaces that I have explored so far in this API and then analyze an official example for animating four Box composables provided in the document.

## **How does it work?**

### Quick Overview

1. **Lookahead pass**: All layouts will first determine their target or destination layout. This allows a pre-calculation of the layout when it changes and use this information to measure and place layouts during each frame of the animation (next point).
    
2. **Approach pass**: Then they will run the measurement and placement approach or logic to reach the destination gradually.
    
3. The **LookaheadScope** interface creates a scope for the above two steps.
    
4. The measurement and placement approach in the **approach pass** is defined by the `Modifier.approachLayout` or by the `ApproachLayoutModifierNode` interface (creating custom approach logic in an explicit Modifier Node).
    

## Let's understand the APIs

### **LookaheadScope**

There is a `LookaheadScope` interface and a `LookaheadScope` composable. The interface is a receiver scope for all child layouts within the composable. It provides access to the `lookaheadScopeCoordinates` from any child's `PlacementScope`. This allows any child to convert `LayoutCoordinates` to the `LayoutCoordinates` in lookahead coordinate space using the `toLookaheadCoordinates()` function. This is particularly useful for animations where we want to calculate positions based on the future state of the layout.

### ApproachMeasureScope

A scope that provides access to the lookahead results. The `ApproachLayoutModifierNode` can leverage these results to define how measurements and placements approach their destination.

### approachLayout Modifier and ApproachLayoutModifierNode

The `approachLayout` modifier is instrumental in managing the **approach pass** (it replaces the now-deprecated `Modifier.intermediateLayout`). It operates within the `LookaheadScope` to establish the methodology for measuring and positioning a composable's content throughout an animation. This modifier constructs an "approach layout" that facilitates a gradual transition towards the target layout defined during the lookahead pass.

Internally, the `approachLayout` modifier utilizes the `ApproachLayoutModifierNode` API, a new Modifier Node tailored for the destination layout as determined in the **lookahead phase**. This Node relies on input from the users to confirm the completion of measurement and placement processes. Such confirmations enable the system to bypass the approach phase once concluded, thereby enhancing layout performance by avoiding unnecessary recalculations.

### DeferredTargetAnimation

The `DeferredTargetAnimation` is a new experimental API for creating those animations whose target is unknown at instantiation. It can be used for size or position animations where the target size or position stays unknown until the later measure and placement phase. It has an `updateTarget` function, which sets up an animation or updates an already running animation. It returns the current value of the animation, after launching the animation in the given `coroutineScope`. It also has an `isIdle` Boolean property which returns true when the animation has finished running and reached its destination, or when the animation has not been set up.

### Some important concepts here:

1. `lookaheadSize`: The destination/target size of the layout obtained in the `ApproachMeasureScope`. This is the size of the `ApproachLayoutModifierNode` measured during the **lookahead pass**.
    
2. `localLookaheadPositionOf`: It calculates the local position in the Lookahead coordinate space. It is used to obtain the target position during placement by using `LookaheadScope.localLookaheadPositionOf` and the lookahead `LayoutCoordinates` in the `ApproachMeasureScope`.
    
3. By knowing the target size and position, animations or other layout adjustments can be defined in the `ApproachLayoutModifierNode` to morph the layout gradually in both size and position to arrive at its precalculated bounds.
    
4. `isMeasurementApproachComplete`: A block that indicates whether the **measurement** has reached the destination size. It is invoked after the destination has been determined by the lookahead pass, before `approachMeasure` is invoked. It receives lookahead size in the argument to decide whether the destination size has been reached.
    
5. `isPlacementApproachComplete`: A block that indicates whether the **position** has approached the destination defined by the lookahead. Based on this, the system can decide whether additional approach placements are necessary. It is invoked after the destination position has been determined by the lookahead pass, and before the placement phase in `approachMeasure`.
    
6. Once both of the above two blocks return `true`, the system may skip **approach pass** until additional approach passes are necessary as indicated by them. If the above two approach callbacks are incomplete for a long time, the system might skip approach pass whenever possible. Hence it’s important to be accurate about these two callbacks.
    

## Implement in code

We have learned a lot of concepts. But we do not know how to use them yet. Let’s write some code with these APIs.

Let’s start with an example where we want to animate four colored Boxes smoothly from a `Row` view to a `Column` view and vice-versa, on the click of the container. This example has been taken from the [official docs here](https://developer.android.com/reference/kotlin/androidx/compose/ui/layout/LookaheadScope) for `LookAheadScope`. We want to create something like this:

![](https://cdn-images-1.medium.com/max/1600/1*y2TcVNniFzCQ1Zb1x2h30w.gif align="left")

**Now, we can follow two ways to use Lookahead APIs:**

1. Write a custom implementation of the `ApproachLayoutModifierNode`
    
2. Use the `approachLayout` Modifier (internally uses `ApproachLayoutModifierNode`)
    

**Both are ideally doing the same thing**: defining an approach or logic to reach the destination. I will show both ways separately to obtain the desired animation shown above.

### 1\. Writing a custom implementation of the `ApproachLayoutModifierNode`

#### **Step 1:Create movable contents and switch layouts**

First, we will create movable contents for the four colored Boxes. For this, we will use the `movableContentOf` API. This enables us to move around content without the need for recomposition. It works by receiving a composable lambda function that it will remember and move to wherever it is invoked. Then using a Boolean mutable state we will toggle them between a `Row` and a `Column` on the click of the parent `Box` container.

```kotlin
@Composable
fun LookAheadWithSimpleMovableContent() {
    val colors = listOf(
        Color(0xffff6f69),
        Color(0xffffcc5c),
        Color(0xff264653),
        Color(0xFF679138),
    )
    var isInColumn by remember { mutableStateOf(true) }
    val items = remember {
        movableContentOf {
            colors.forEach { color ->
                Box(
                    Modifier
                        .padding(8.dp)
                        .size(80.dp)
                        .background(color, RoundedCornerShape(10))
                )
            }
        }
    }

    Box(
        modifier = Modifier.fillMaxSize().clickable { isInColumn = !isInColumn },
        contentAlignment = Alignment.Center
    ) {
        if (isInColumn) {
            Column { items() }
        } else {
            Row { items() }
        }
    }
}
```

This will create a behavior like this. The layouts are switching but there is no animation as we have not added any.

![](https://cdn-images-1.medium.com/max/1600/1*X5tbelHoD64RU0HReLIssg.gif align="left")

**Step 2: Use the**`LookaheadScope`**API**

Next, we will wrap our content with the `LookaheadScope` composable. This will provide us with the scope to “look ahead” so that we can determine the destination of our Box layouts through a lookahead pass.

So, our code will now look like this:

```kotlin
@Composable
fun LookAheadWithSimpleMovableContent() {
    // ...
    var isInColumn by remember { mutableStateOf(true) }
    LookaheadScope { // Wrapped with LookaheadScope
        // ...
        val items = remember {
            movableContentOf {
                // ...
            }
        }

        Box(
            modifier = Modifier.fillMaxSize().clickable { isInColumn = !isInColumn },
            contentAlignment = Alignment.Center
        ) {
            // Remaining code
        }
    }
}
```

#### Step 3: A custom implementation of `ApproachLayoutModifierNode`

Now the most crucial part. We will use the `LookaheadScope` from the above step to create a custom implementation of the `ApproachLayoutModifierNode`.

For this, we have to override 3 functions:

1. `isMeasurementApproachComplete`
    
2. `isPlacementApproachComplete`
    
3. `approachMeasure`
    

First, we have to create an offset animation of the type `DeferredTargetAnimation`, the target of which will be known during placement.

```kotlin
private val offsetAnimation: DeferredTargetAnimation<IntOffset, AnimationVector2D> = 
    DeferredTargetAnimation(IntOffset.VectorConverter)
```

Then override `isMeasurementApproachComplete`. As we are only animating the placement here, we can consider the measurement approach complete. So we can simply return a `true` here.

```kotlin
override fun isMeasurementApproachComplete(lookaheadSize: IntSize): Boolean {
    return true
}
```

Now we’ll override the `isPlacementApproachComplete`.

```kotlin
override fun Placeable.PlacementScope.isPlacementApproachComplete(
    lookaheadCoordinates: LayoutCoordinates
): Boolean {
    val target: IntOffset = with(lookaheadScope) {
        lookaheadScopeCoordinates.localLookaheadPositionOf(lookaheadCoordinates).round()
    }
    offsetAnimation.updateTarget(target, coroutineScope)
    return offsetAnimation.isIdle
}
```

**Let’s try to understand what is happening in the above code.** Our main objective of this function is to return `true` when the offset animation is complete, and `false` otherwise. First, we are acquiring the `target` position of the layout using the `localLookaheadPositionOf` with the layout’s coordinates. We have discussed previously that `localLookaheadPositionOf` calculates the local position of the layout in the Lookahead coordinate space. Then we will call `updateTarget` on the `offsetAnimation` that we created at the beginning with this acquired `target` position. This function will set up the animation or will update the already running animation based on the `target`. Finally, we will return `offsetAnimation.isIdle` which will return `true` when the animation has finished running.

Now, we’ll override the `approachMeasure`.

```kotlin
override fun ApproachMeasureScope.approachMeasure(
    measurable: Measurable,
    constraints: Constraints
): MeasureResult {
    val placeable = measurable.measure(constraints)
    return layout(placeable.width, placeable.height) {
        val coordinates = coordinates
        if (coordinates != null) {
            val target = with(lookaheadScope) {
                lookaheadScopeCoordinates.localLookaheadPositionOf(coordinates).round()
            }
            val animatedOffset = offsetAnimation.updateTarget(target, coroutineScope)
            val placementOffset = with(lookaheadScope) {
                lookaheadScopeCoordinates.localPositionOf(coordinates, Offset.Zero).round()
            }
            val (x, y) = animatedOffset - placementOffset
            placeable.place(x, y)
        } else {
            placeable.place(0, 0)
        }
    }
}
```

**Let’s try to understand what is happening in the above code.** The function `approachMeasure` accepts a `Measurable` and `Constraints` and returns a `MeasureResult`. So we are first measuring our measurable (which is our layout) using the constraints. This generates a `Placeable`. By definition, we know that a Placeable corresponds to a child layout that can be positioned by its parent layout. So we need to position or `place` this Placeable. Now, similar to the previous step, we are calculating the target offset within the `lookaheadScope`, using the available`LayoutCoordinates`. Then using the `updateTarget` function and the `target` offset, we are starting an offset animation. This gives us the animated offset or position. Then, we are calculating the current offset within the given `LookaheadScope` using the `localPositionOf` function. We then calculate the delta between the animated position and the current position in `lookaheadScope`. Finally, we put the child layout in the animated position using the `place` function.

> Both `localLookaheadPositionOf` and `localPositionOf` are used to get the converted offset relative to a specific coordinate. The only difference is that unlike `localPositionOf`, `localLookaheadPositionOf` uses the lookahead position for coordinate calculation.

Till here we are done with the custom implementation of the `ApproachLayoutModifierNode` and overridden all three functions.

#### Step 4: Create a custom node element

Now we have to create a custom node element for the `AnimatedPlacementModifierNode` created above by implementing the `ModifierNodeElement` abstract class. It takes a `LookaheadScope` instance in the constructor and creates/updates our custom `ApproachLayoutModifierNode`.

```kotlin
data class AnimatePlacementNodeElement(val lookaheadScope: LookaheadScope) :
    ModifierNodeElement<AnimatedPlacementModifierNode>() {

    override fun update(node: AnimatedPlacementModifierNode) {
        node.lookaheadScope = lookaheadScope
    }

    override fun create(): AnimatedPlacementModifierNode {
        return AnimatedPlacementModifierNode(lookaheadScope)
    }
}
```

#### Step 5: Create a `Modifier` to use the custom node element

We will create a `Modifier` that will internally use the custom node element `AnimatePlacementNodeElement` created above. This Modifier will be used with the colored Boxes that we want to animate.

```kotlin
fun Modifier.animatePlacementInScope(lookaheadScope: LookaheadScope): Modifier {
    return this.then(AnimatePlacementNodeElement(lookaheadScope))
}
```

#### **Step 6: Use the above-created**`Modifier`

Going back to the initial code we wrote, we will use this Modifier with the movable content Boxes. And we will pass the current `LookaheadScope` instance using this as we already wrapped it inside the `LookaheadScope` composable.

```kotlin
@Composable
fun LookAheadWithSimpleMovableContent() {
    // ...
    var isInColumn by remember { mutableStateOf(true) }
    LookaheadScope {
        val items = remember {
            movableContentOf {
                colors.forEach { color ->
                    Box(
                        Modifier
                            .padding(8.dp)
                            .size(80.dp)
                            .animatePlacementInScope(this) // Add Modifier here
                            .background(color, RoundedCornerShape(10))
                    )
                }
            }
        }
      // Remaining code
    }
}
```

Our implementation of Lookahead APIs with movable content is completed and now we will see the desired behavior. A smooth animation of the four Boxes between `Row` to `Column` layouts.

![](https://cdn-images-1.medium.com/max/1600/1*y2TcVNniFzCQ1Zb1x2h30w.gif align="left")

**See the complete code for this example here:**[https://github.com/pushpalroy/ComposeLayoutPlayground/…/LookAheadWithCustomApproachLayoutModifierNode.kt](https://github.com/pushpalroy/ComposeLayoutPlayground/blob/main/app/src/main/java/com/appmason/composelayoutplayground/ui/screens/lookaheadlayout/LookAheadWithCustomApproachLayoutModifierNode.kt)

### 2\. **Using the** `approachLayout` Modifier

This approach will be very similar to the previous way, with some differences.

#### Step 1: Create movable contents with `LookaheadScope` receiver

In the former approach, we used the `movableContentOf` API. In this approach, we will use the `movableContentWithReceiverOf` API. This is also used to create movable content but with a receiver context, which in our case will be the `LookaheadScope`. So our previous code to initialize the items of colored Boxes will become:

```kotlin
val items = remember {
    movableContentWithReceiverOf<LookaheadScope> {
        colors.forEach { color ->
            Box(
                Modifier
                    .padding(8.dp)
                    .size(80.dp)
                    .background(color, RoundedCornerShape(10))
            )
        }
    }
}
```

The remaining code for switching the layout between `Row` and `Column` will remain the same as the former code.

#### Step 2: Create a Modifier using the `approachLayout` Modifier

This step is very similar to how we created the custom implementation of `ApproachLayoutModifierNode` before. The `approachLayout` Modifier also provides us the three lambdas to invoke: `isMeasurementApproachComplete`, `isPlacementApproachComplete` and `approachMeasure`. This is exactly the same as how we have overridden the three functions previously. Also, we are going to write the same login inside each of these lambdas for our use case.

Note how we marked the entire Modifier function with `context (LookaheadScope)` at the top. This means our `animateBounds` Modifier will only work in the context of a `LookaheadScope`. This also helps to access the `lookaheadScopeCoordinates` for calculating the `localPosition` in the Lookahead coordinate space.

```kotlin
context (LookaheadScope)
@OptIn(ExperimentalAnimatableApi::class, ExperimentalComposeUiApi::class)
fun Modifier.animateBounds(): Modifier = composed {
    val offsetAnim = remember { DeferredTargetAnimation(IntOffset.VectorConverter) }
    val scope = rememberCoroutineScope()
    this.approachLayout(
        isMeasurementApproachComplete = {
            true
        },
        isPlacementApproachComplete = {
            val target = lookaheadScopeCoordinates.localLookaheadPositionOf(it)
            offsetAnim.updateTarget(target.round(), scope)
            offsetAnim.isIdle
        }
    ) { measurable, constraints ->
        measurable.measure(constraints)
            .run {
                layout(width, height) {
                    coordinates?.let {
                        val target = lookaheadScopeCoordinates.localLookaheadPositionOf(it).round()
                        val animOffset = offsetAnim.updateTarget(target, scope)
                        val current = lookaheadScopeCoordinates.localPositionOf(it, Offset.Zero).round()
                        val (x, y) = animOffset - current
                        place(x, y)
                    } ?: place(0, 0)
                }
            }
    }
}
```

**Note:** If we [see the internals](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LookaheadScope.kt;l=138?q=Modifier.approachLayout&sq=) of the `approachLayout` Modifier, we will find that it is in fact creating a `ApproachLayoutElement` data class which is an implementation of the `ModifierNodeElement` abstract class (Step 4 of our former way). And a custom implementation of `ApproachLayoutModifierNode` is there, called the `ApproachLayoutModifierNodeImpl` (Step 3 of our former way). So basically both the ways we just covered do the same thing.

#### Step 3: Use the above-created Modifier in our layout

Now the obvious step, we will use the animateBounds modifier in our layout. This is similar to Step 6 of our former way. As we used `movableContentWithReceiverOf<LookaheadScope>` the `animateBounds()` will execute in the context of `LookaheadScope`, which we need.

```kotlin
// Remaining Code
// ..
val items = remember {
    movableContentWithReceiverOf<LookaheadScope> {
        colors.forEach { color ->
            Box(
                Modifier
                    .padding(8.dp)
                    .size(80.dp)
                    .animateBounds() // Use modifier here
                    .background(color, RoundedCornerShape(10))
            )
        }
    }
}
// ..
// Remaining Code
```

All the other codes will remain the same. If we run the above code it will result in the same animation we obtained earlier.

**See the complete code for this example here:**

[https://github.com/pushpalroy/ComposeLayoutPlayground/…/LookAheadWithApproachLayoutModifier.kt](https://github.com/pushpalroy/ComposeLayoutPlayground/blob/main/app/src/main/java/com/appmason/composelayoutplayground/ui/screens/lookaheadlayout/LookAheadWithApproachLayoutModifier.kt)

It’s obvious that the second approach, that is using the `approachLayout` Modifier is an easier way and requires writing less code for our use case.

## Customize animations

In the `isPlacementApproachComplete` lambda, we are updating the offset animation using the `updateTarget` function:

```kotlin
offsetAnim.updateTarget(target.round(), scope)
```

The `updateTarget` has a default `animationSpec` of `spring()`, which we can customize. For example, we can pass a `tween()` like this:

```kotlin
offsetAnim.updateTarget(target.round(), scope, tween(durationMillis = 800))
```

This will create an animation like this:

![](https://cdn-images-1.medium.com/max/1600/1*gxByJjVPbZux28y3p3I_IQ.gif align="left")

## Animate size along with placement

If we remember, in the previous animation we have just animated the placement of the Boxes. As we only animated the placement here and not the size, we considered the measurement was approach complete. So we returned a `true` inside the `isMeasurementApproachComplete` lambda.

What if we want to animate the size as well? Let’s do that.

Let’s modify the last code and switch the size of each `Box` between `80.dp` and `20.dp` based on whether the layout is a `Row` or `Column`.

```kotlin
// ..
var isInColumn by remember { mutableStateOf(true) }
val items = remember {
    movableContentWithReceiverOf<LookaheadScope> {
        colors.forEach { color ->
            Box(
                Modifier
                    .padding(8.dp)
                    .size(if (isInColumn) 80.dp else 20.dp)
                    .animateBounds()
                    .background(color, RoundedCornerShape(10))
            )
        }
    }
}
// Remaining Code
```

So now based on the `isInColumn` flag, we are changing the size. That means if the layout is a Column `80.dp` will be used else `20.dp` will be used.

Also, let’s increase the animation duration of the offset inside `isPlacementApproachComplete` so that we see clearly what is happening. Let's use a `tween` animation for 3000ms.

```kotlin
// ..
isPlacementApproachComplete = {
    val target = lookaheadScopeCoordinates.localLookaheadPositionOf(it)
    offsetAnim.updateTarget(target.round(), scope, tween(3000))
    offsetAnim.isIdle
}
// ..
```

Now without any further modification, if we run the code now, we will see this:

![](https://cdn-images-1.medium.com/max/1600/1*b9vj64n7yVI9bZV3BJ1Fmw.gif align="left")

If we look closely, the size is changing but there is no animation for that. We can only see animation for placement. The size is just snapping from `80.dp` to `20.dp` quickly.

**Let’s add animation for the size change:**

Similar to the `offsetAnim`, we will create a `DeferredTargetAnimation` called the `sizeAnim`, which will track the animation for size. Then instead of returning `true` inside `isMeasurementApproachComplete`, now we will do `updateTarget` on `sizeAnim` and return if it’s idle:

```kotlin
// ..
this.approachLayout(
  isMeasurementApproachComplete = {
      sizeAnim.updateTarget(it, scope, tween(2000))
      sizeAnim.isIdle
  },
  isPlacementApproachComplete = {
    // Same as before
  }
// ..
```

The above code will return `true` inside `isMeasurementApproachComplete` when the size animation is complete. Note that we have used a `tween` animation of duration 2000ms for the size animation.

Now, inside the `approachMeasure` block, we will do `sizeAnim.updateTarget` with the `lookaheadSize` to get the `animWidth` and `animHeight`. This is the measured size of the layout while animating. We will then use this constraint to measure the measurable: `Constraints.fixed(animWidth, animHeight)`.

```kotlin
this.approachLayout(
    isMeasurementApproachComplete = {
        // ..
    },
    isPlacementApproachComplete = {
        // ..
    }
) { measurable, _ ->
    val (animWidth, animHeight) = sizeAnim.updateTarget(lookaheadSize, scope)
    measurable.measure(Constraints.fixed(animWidth, animHeight))
        .run {
            layout(width, height) {
                coordinates?.let {
                    val target = lookaheadScopeCoordinates.localLookaheadPositionOf(it).round()
                    val animOffset = offsetAnim.updateTarget(target, scope)
                    val current = lookaheadScopeCoordinates.localPositionOf(it, Offset.Zero).round()
                    val (x, y) = animOffset - current
                    place(x, y)
                } ?: place(0, 0)
            }
        }
}
```

This will create an effect like this:

![](https://cdn-images-1.medium.com/max/1600/1*rlDMkVy7Kk1bO2HAmWfBTg.gif align="left")

Now we can see that the Boxes' size and placement are getting animated smoothly.

**See the complete code for this example here:**

[https://github.com/pushpalroy/ComposeLayoutPlayground/…/LookAheadWithApproachLayoutModifier2.kt](https://github.com/pushpalroy/ComposeLayoutPlayground/blob/main/app/src/main/java/com/appmason/composelayoutplayground/ui/screens/lookaheadlayout/LookAheadWithApproachLayoutModifier2.kt)

---

You can find all the examples along with some other examples of Lookahead in this repository:

%[https://github.com/pushpalroy/ComposeLayoutPlayground] 

## Some important API changes

1. [In **Version 1.7.0-al**](https://github.com/pushpalroy/ComposeLayoutPlayground/blob/main/app/src/main/java/com/appmason/composelayoutplayground/ui/screens/lookaheadlayout/LookAheadWithApproachLayoutModifier2.kt)[**pha03**](https://developer.android.com/jetpack/androidx/releases/compose-ui#1.7.0-alpha03), the `ApproachLayoutModifierNode` API [has been added to support creating custom approach lo](https://github.com/pushpalroy/ComposeLayoutPlayground/blob/main/app/src/main/java/com/appmason/composelayoutplayground/ui/screens/lookaheadlayout/LookAheadWithApproachLayoutModifier2.kt)gic in an explicit Modifier Node.
    
2. In [**Version 1.6.0-alpha03**](https://developer.android.com/jetpack/androidx/releases/compose-animation#1.6.0-alpha03), the `LookaheadScope` composable and interfaces are made stable. Also, a new enter/exit transition to scale content to container size has been added. [Read here](https://developer.android.com/jetpack/androidx/releases/compose-animation#1.6.0-alpha03).
    
3. In [**Version 1.6.0-alpha02**](https://developer.android.com/jetpack/androidx/releases/compose-ui#1.6.0-alpha02), `LookaheadLayout` and `LookaheadLayoutScope` have been finally removed and replaced by `LookaheadScope` APIs.
    
4. In [**Version 1.6.0-alpha01**](https://developer.android.com/jetpack/androidx/releases/compose-ui#1.6.0-alpha01), support for lookahead has been added to `LazyList`.
    
5. In [**Version 1.5.0-alpha03**](https://developer.android.com/jetpack/androidx/releases/compose-ui#1.5.0-alpha03), new behavior has been added due to which `SubcomposeLayouts` that don’t have conditional slots work nicely with lookahead animations.
    
6. In [**Version 1.5.0-alpha01**](https://developer.android.com/jetpack/androidx/releases/compose-ui#1.5.0-alpha01), `LookaheadLayout` has been replaced by `LookaheadScope`, which is no longer a Layout.
    
7. In [**Version 1.3.0-alpha01**](https://developer.android.com/jetpack/androidx/releases/compose-ui#1.3.0-alpha01), `LookaheadLayout` was first introduced. See the [commit here](https://android-review.googlesource.com/c/platform/frameworks/support/+/1961800).
    

## Additional resources

Learn more about LookAheadLayout from these resources:

1. **Official Docs:**  
    [https://developer.android.com/reference/kotlin/androidx/compose/ui/layout/LookaheadScope](https://developer.android.com/reference/kotlin/androidx/compose/ui/layout/LookaheadScope)
    
2. **Introducing LookaheadLayout by**[**Jorge Castillo**](https://twitter.com/JorgeCastilloPr)\*\*:  
    \*\*[https://substack.com/inbox/post/64358322](https://substack.com/inbox/post/64358322)
    
3. This blog by [**Ji Sungbin**](https://jisungbin.medium.com/?source=user_profile-------------------------------------):   
    [https://betterprogramming.pub/introducing-jetpack-composes-new-layout-lookaheadlayout-eb30406f715](https://betterprogramming.pub/introducing-jetpack-composes-new-layout-lookaheadlayout-eb30406f715)
    

## **Conclusion**

Lookahead is a potent API within Jetpack Compose that, when utilized correctly, enables the creation of stunning and highly performant animations, including shared element transitions among others. This article aims to provide an initial exploration and shed light on various aspects of this new API, encouraging further experimentation.

Check out the follow-up article where I demonstrate how to create a container transform animation using Lookahead: [**Container Transform Animation with Lookahead in Jetpack Compose**](https://hashnode.com/post/cltqaexhy000309jx3nnu04s3)

Follow me: [@pushpalroy](https://twitter.com/pushpalroy)
