I recently had the opportunity to work on a custom “Slide to Book” button for one of my projects and since this was my first time implementing a swipe-to-action animation in Jetpack Compose, I thought I’d share the learnings and breakdown the implementation.
This button allows the user to drag a slider thumb horizontally to confirm a booking. Once dragged past a threshold, the button triggers a ride booking action, plays a subtle animation, and displays a loading indicator.

If you would like to skip this article and just want to see a gif of the end result and go straight for the final code, here’s the link.
Let’s get’s started!
Step 1: Create the Composable
We start by defining the top-level composable for the button. This composable is responsible for the layout and structure of the entire slider: the outer button that holds the text and slider, and the inner slider thumb that the user can drag. Let’s name it SlideToBookButton.
This composable includes:
btnText: The label displayed on the outer track (e.g., “Book Ride ₹199”).
btnTextStyle : The typography style for the btnText — allows font weight, size, etc.
outerBtnBackgroundColor : The background color for the entire button track.
sliderBtnBackgroundColor : The background color for the draggable slider/thumb element.
sliderBtnIcon : The icon displayed inside the slider button.
onBtnSwipe : The callback invoked when the user successfully completes the swipe.
Step 2: Define the button UI
With our parameters in place, the next step is to define the visual structure of the SlideToBookButton. The layout has two primary components:
- The outer button— a full-width container that holds the background and the center aligned button text.
- The slider button.thumb — a smaller draggable button layered above the track that users can slide horizontally.

Step 3: Add Drag gesture and slide animation
Now that we’ve built the UI for our “Slide to Book” button, let’s start with the animation. We want users to drag the slider thumb from left to right, and once they’ve swiped far enough, we’ll consider it a successful “slide to book” action.
To animate and track the thumb’s position during drag, we’ll need to:
- Figure out how wide the button is: We use something called
Modifier.onSizeChanged
to get the actual width of the outer button once it’s displayed. This tells us how far the slider thumb is allowed to move — from the left edge to just before the right edge. - Track how far the thumb moves: We use
remember { mutableStateOf(… ) }
to store the current X-position of the slider thumb in pixels. This allows us to update the thumb’s position reactively as the user drags. - Listen for drag gestures: We use
Modifier.draggable
withrememberDraggableState { delta -> … }
to listen for drag gestures in a specified direction (in our case, horizontally). Thedelta
parameter gives us the amount the user’s finger moved since the last update — in pixels, not dp. This means that, every time the user drags their finger left or right, delta tells us how far they moved. We take thatdelta
and add it to our current thumb position, so the thumb follows the finger. If they swipe right, the thumb moves right. If they try to go left again, it moves left — just like dragging a real slider. We also make sure the thumb doesn’t go past the edges. This gives us full control over the drag behavior — not just how far the user can swipe, but when the swipe is “complete”, and even what happens if we want to animate it back later. - Finally, we move the thumb visually on the screen: We use Modifier.offset to move a composable by a certain distance on the X (horizontal) or Y (vertical) axis — like nudging it left, right, up, or down. As the user drags their finger, we’re tracking how far they’ve moved using sliderPositionPx (in pixels). We then pass that value to
offset
— after converting it to dp, sinceoffset
expects dp values. This is what makes the slider thumb visually shift across the button, matching the user’s drag. Without offset, even though we’re tracking the finger movement, the thumb would just sit still.
Now let’s update our code:
This is what we’re achieved so far:

Step 4: Add text alpha animation
Now that we’ve wired up the drag gesture for the slider button, it’s time to move on to fading out the button text as the slider is dragged. As the user starts sliding: The button label (e.g., “Book Ride ₹199”) gradually fades out. When the slider reaches the end, the text should be completely invisible.
To fade out the text label, we’ll need to:
- measure how far the slider button has been dragged. That gives us a number from 0.0 (not moved at all) to 1.0 (moved all the way to the end). This value is what we call
dragProgress
. - Next, we define and calculate the
textAlpha
, which controls how transparent the text is. When dragProgress is 0.0 (user hasn’t moved), textAlpha is 1.0 → the text is fully visible. As the user drags and dragProgress increases (like 0.3, 0.5, 0.7…), we decrease the textAlpha to make the text less visible. When dragProgress reaches 1.0 (fully dragged), textAlpha becomes 0.0 → the text is fully invisible. We apply this textAlpha to ourText
composable usingModifier.alpha()
— so the text fades smoothly while dragging.

This is what we have so far
Step 5: Add track scale & thumb alpha animation
Once the user slides the button almost to the end, we want two things to happen:
- The outer track (button background) should shrink and disappear.
- The slider button should fade out completely.
This tells the user that the slide is complete, and the action (like booking the ride) is being processed.
What we need to do:
- We use a boolean flag
sliderComplete
that tells us when slide progress has passed a threshold. WhendragProgress
reaches 80% or more, we setsliderComplete = true.
- We use
animateFloatAsState()
andModifier.graphicsLayer()
to smoothly animate the scale and alpha changes.

And this is what we’ec achieved!
Note: The order of modifiers matters here. We have to ensure that we add the
.graphicsLayer above the
.background here to ensure that the scaling takes place correctly. If we change the order, i.e. if we add the
.background after we add the
.graphicsayer, the background is already drawn at full size so the scaling doesn’t affect what’s already been rendered. But if you shrink before, everything is drawn inside the new size — and that’s what you want here!
Step 6: Add the loading indicator animation
Now that we have all the moving parts in place, we simply need to show a loading indicator when the animation is complete. For that, we need to:
- Define a boolean flag
showLoading that tells us when we need to show this loading indicator.
- We need to update our
LaunchedEffect(dragProgress) method to set
showLoading = true when sliding is completed.
- We need to display a
CircularProgressIndicator() when
showLoading = true.
And this is our full code:
And that’s pretty much it!
Job Offers
If you found this useful, feel free to share or reach out with feedback/questions! You can follow more of my Jetpack Compose experiments here:
This article was previously published on proandroiddev.com.