Enhance in-app purchase experiences with slide to unlock in Jetpack Compose
In this article, you’ll explore the open-source slide-to-unlock library, built by RevenueCat, and learn how to integrate it with RevenueCat’s in-app purchases in Jetpack Compose

When Apple first introduced the “Slide to Unlock” feature on the iPhone, it wasn’t just a quirky design, it was a deliberate interaction choice. Compared to a normal button, sliding offered a more engaging and meaningful way to unlock a device.
While newer devices have replaced “Slide to Unlock” with swipe gestures or biometric unlocking, the underlying principle remains the same: interaction should feel intentional, intuitive, and rewarding. Apple’s slider was one of the earliest examples of how even a simple UI decision can influence user engagement.
In this article, you’ll explore the open-source slide-to-unlock library, built by RevenueCat, and learn how to integrate it with RevenueCat’s in-app purchases in Jetpack Compose through practical, real-world examples.
Slide to unlock in Jetpack Compose
The slide-to-unlock library is designed for Kotlin Multiplatform, so you can use it in both Android and KMP projects. To add slide-to-unlock to your project, include the following dependency in your Gradle file:
1implementation("com.revenuecat.purchases:slide-to-unlock:1.0.2")
For Kotlin Multiplatform, add the dependency below to your module’s build.gradle.kts file:
1sourceSets {
2 val commonMain by getting {
3 dependencies {
4 implementation(libs.compose.slidetounlock)
5 }
6 }
7}
8
The usage is pretty simple. You can just implement it by using the `SlideToUnlock` composable with creating a state at the call site like below:
1var isSlided by remember { mutableStateOf(false) }
2
3SlideToUnlock(
4 isSlided = isSlided,
5 modifier = Modifier.fillMaxWidth(),
6 onSlideCompleted = { isSlided = true },
7)
SlideToUnlock
is highly customizable, you can adjust nearly every aspect of the slider, including colors, thumb, hint text, gesture behavior, and tracking slide fractions. You can also provide your own composables for the thumb and hint to achieve a fully tailored experience. For more details on customization, please refer to the documentation.
RevenueCat integration
This library supports RevenueCat integration, allowing you to easily implement features like Slide to Purchase or Slide to Subscribe by adding the dependency below:
1implementation("com.revenuecat.purchases:slide-to-unlock-purchases:1.0.2")
You can now use the SlideToPurchases
component, which triggers in-app purchases once the slide action is completed. Its usage is very similar to the SlideToUnlock
composable, but it requires a few additional parameters needed to complete purchases through the RevenueCat SDK.
The library provides several overloaded SlideToPurchases
composables to cover different purchase scenarios, such as product types, subscription upgrades, and promotional offers, while abstracting away the underlying purchase logic.The most common use case is simple: fetch your offerings from RevenueCat and pass the desired Package
to the composable. The component takes care of everything else.
1// inside your coroutine scope
2val currentOffering = Purchases.sharedInstance.awaitOfferings().current
3val monthlyPackage = currentOffering?.getPackage("monthly")
4
5if (monthlyPackage != null) {
6 var purchaseState by remember { mutableStateOf<PurchaseState?>(null) }
7
8 SlideToPurchases(
9 packageToPurchase = monthlyPackage,
10 modifier = Modifier
11 .fillMaxWidth()
12 .padding(16.dp),
13 onPurchaseStateChanged = { newPurchaseState ->
14 purchaseState = newPurchaseState
15 // Handle state changes, e.g., show a dialog on success/error
16 }
17 )
18
19 // Optionally display the state
20 when (val state = purchaseState) {
21 is PurchaseState.Loading -> { /* Show a loading indicator */ }
22 is PurchaseState.Success -> { /* Navigate to a success screen */ }
23 is PurchaseState.Error -> { /* Show an error message */ }
24 null -> { /* Initial state */ }
25 }
26}
Similar to the Purchases.sharedInstance.awaitPurchase()
function, you can perform in-app purchases using several options: StoreProduct
, Package
, SubscriptionOption
, PromotionalOffer
, and WinBackOffer
. For more details, check out the overloads documentation.
Building a custom paywalls with slide to unlock
Now let’s take a look at how to build a custom paywall that includes Slide to Unlock. If you want to create your own custom paywalls, you can achieve it by using RevenueCat’s Paywall Editor, which is based on a server-driven UI.
However, if you need a more personalized design, such as containing very customized UI components that aren’t supported by the Paywall Editor, you’ll need to create your own custom paywalls. In that case, you can manually fetch the current offerings and render the screens based on your requirements.
Currently, RevenueCat’s Paywall Editor doesn’t support Slide to Unlock officially, so in this article we’ll walk through a workaround for integrating it into your custom paywalls. First things first: open the RevenueCat Paywall Editor and create a new paywall without a footer, leaving enough space at the bottom like the image below:

Next, fetch the current offering as shown in the code below:
1try {
2 val offerings = Purchases.sharedInstance.awaitOfferings()
3 offerings.current?.let { currentOffering ->
4 // you have a current offering now here
5 }
6} catch (e: PurchasesException) {
7 // fetching offering exception
8}
Then, retrieve the available package information from the offering and position the Paywall
composable component alongside SlideToUnlock
, as demonstrated here:
1 Box(
2 modifier = Modifier
3 .fillMaxSize()
4 .background(Color.White),
5 ) {
6 Paywall(
7 options = PaywallOptions.Builder(
8 dismissRequest = { viewModel.navigateUp() },
9 ).setOffering(offering).build(),
10 )
11
12 val availablePackage = offering?.availablePackages?.first()
13 val activity = (LocalContext.current as? Activity)
14 var isSlided by remember { mutableStateOf(false) }
15
16 if (availablePackage != null && activity != null) {
17 SlideToUnlock(
18 modifier = Modifier
19 .align(Alignment.BottomCenter)
20 .fillMaxWidth()
21 .padding(bottom = 60.dp, start = 20.dp, end = 20.dp),
22 isSlided = isSlided,
23 hintTexts = HintTexts.defaultHintTexts().copy(
24 defaultText = stringResource(
25 com.revenuecat.articles.paywall.compose.core.designsystem.R.string.slide_subscribe,
26 ),
27 ),
28 onSlideCompleted = {
29 isSlided = true
30 viewModel.handleEvent(
31 PaywallEvent.Purchases(
32 activity = activity,
33 availablePackage = availablePackage,
34 ),
35 )
36 },
37 )
38 }
39 }
40
That’s it! You’ll now see the result below, a seamless combination of the paywall you built in the Paywall Editor and the Slide to Unlock component.

You can check out the full source code on GitHub, an open source project Cat Paywall Compose’s paywalls directory.
Wrapping Up
In this article, you’ve learned how to use the slide-to-unlock library and integrate it into a custom paywall. While RevenueCat’s Paywall Editor offers a fast and convenient way to build paywalls, sometimes you’ll want to go further and experiment with more personalized or playful interactions.
There are many strategies to increase user engagement and improve subscription conversion rates, clear value communication, compelling design, optimized pricing, and interactive UI patterns all play a role. Slide to Unlock adds an extra layer of interactivity that feels intuitive, deliberate, and rewarding for users, helping to reduce friction and increase commitment to the purchase flow.
As you continue refining your paywalls, consider testing different designs, messaging, and engagement patterns to find what resonates best with your audience. Combined with RevenueCat’s analytics and A/B testing capabilities, approaches like slide-to-unlock can be great tools in building subscription experiences that are not only effective but also enjoyable.
You might also like
- Blog post
Mark your models as stable with the Compose runtime annotation library
In this article, we’ll look at how to address this issue using the new compose-runtime-annotation library.
- Blog post
remember vs rememberSaveable: deep dive into state management and recomposition in Jetpack Compose
Understanding the differences between remember and rememberSaveable by exploring their internal mechanisms, and how they relate to state and recomposition.
- Blog post
Server-driven UI SDK on Android: how RevenueCat enables remote paywalls without app updates
Learning server-driven UI by exploring RevenueCat's Android SDK.