Exploring Jetpack Compose Anchored Draggable Modifier

Simplify draggable content with the new anchoredDraggable modifier from Jetpack Compose 1.6.0-alpha01

In this short article we will learn how to use the new anchoredDraggable modifier released with Jetpack Compose Foundation 1.6.0-alpha01 .

anchoredDraggable is a new modifier that allows us to drag content either horizontally or vertically, specifying optional anchor points along the drag zone where the content will snap to. Until now if you wanted to drag some content along the screen you would define a pointerInput and have to handle the drag gestures yourself; with anchoredDraggable this becomes much simpler.

Using the correct dependencies

Before we start, we will need to specify the correct dependencies for our build. If you use the compose BOM to specify your Jetpack Compose dependencies, you will need to add an additional line to your build.gradle file to import the necessary dependencies for the anchoredDraggable modifier, as the BOM only pulls in stable dependencies. The line you need to add to your gradle file is


Note that the foundation library has been compiled against SDK 34, so you will also need to set your compileSdk to 34 in the same gradle file.

How the anchored draggable modifier works

The new anchored draggable has 2 main parts, one is the Modifier that is applied to the content to be dragged, and the other is its state, AnchoredDraggableState, which specifies how the drag will operate.

The draggable state

Let’s start by looking at the state. We get an instance of the state by using its constructor, defined as shown below:

In this constructor, we have

It’s worth of note that there is no rememberDraggableState factory method currently available, so we will need to remember the state in our composables manually.

Besides the constructor, there are a couple of other APIs that we need to familiarise ourselves with in order to use the anchoredDraggable modifier, these are updateAnchors and requireOffset. Let’s have a look.

We use the method updateAnchors to specify the stop points along the drag zone where the content will snap to. At a minimum, we need to specify 2 anchors so that the content can be dragged between those 2, but we can add as many as we need. There is also a helper method that simplifies creating the anchors, called DraggableAnchors that we will later use.

The other method we will be using is requireOffset, this method simply returns the offset of the draggable content, so that we can apply it to the content. Note that the anchoredDraggable modifier does not actually move the content on drag, it simply computes the offset when the user drags on the screen, it is our responsibility to update the content based on the offset provided by requireOffset. We will see how we can do that later in an example.

The anchoredDraggable modifier

The modifier itself is very simple:

This modifier accepts;

Using the anchored draggable modifier

Fixed anchors

Our first example will use just 2 fixed anchors, a start and end position for the content to drag.

To specify the drag points, we will create an enum to specify the 2 anchors,

Then, in our composable where we want to have the draggable content, we instantiate the state,

Now that we have the state, we can apply the modifier to our content, let’s see how

This is pretty much all that is required, the full code is shown here:

With this we get a draggable content that will snap at either the left edge of the screen (0 pixels of offset), or at 400 pixels:

Dynamic Anchors

The example above used 2 anchors at fixed locations, let’s see how we can update the example to use anchors that leverage the screen width instead.

First we will update our enum to specify a 3rd anchor point, halfway between the start and end. The enum will also provide a fraction that we will use to compute the offset, this will be a fraction of the overall screen width:

As described in the documentation of anchoredDraggable, to use dynamic anchors that depend on the size we need to use the onSizeChanged modifier so that we can read the width available to our composable, so that we can then in turn calculate the correct anchor points. Let’s see how we can do that:

The draggable content, inside the Box, does not change. The full code is show below:

We can also make this a bit more playful by using a spring animation with a slight bounce, all that is required is to update our animation spec:

Vertical drag

Just as we can drag horizontally, we can do the same vertically, all it requires is to change the offset modifier to update the y coordinate instead of the x one, and to specify the new direction when we set the anchoredDraggable modifier. These changes are minor, so I’ll just show the final code here (note that I added 2 additional anchor points):

Saving the state

Something you may have noticed is that, if we trigger a configuration change (by rotating the device, or changing the theme), the draggable content resets to its original position, it does not retain its last dragged to position. This is because we are remembering the state, which only survives as long as the composable is in the composition, and is lost when it leaves the composition, as happens on configuration change.

To fix this we can use rememberSaveable and use the Saver provided by the state itself, which is defined as shown below

So to persist the draggable state across state changes we need to change our remember call to a rememberSaveable and provide the Saver shown above. The Saver accepts the same lambdas as the state itself, so we can extract those to helper properties in our composable and refactor our code as shown below:

We have to keep in mind that, if we are using dynamic anchors then we would need to use a key on the saver using the density, so that the state is recreated should the density change. The same applies to any other property that you may use for the state and anchors.

This concludes this article, the sample code is available on this gist.