{"id":30660790,"url":"https://github.com/revenuecat/slide-to-unlock","last_synced_at":"2025-08-31T14:13:41.622Z","repository":{"id":311087912,"uuid":"1042414316","full_name":"RevenueCat/slide-to-unlock","owner":"RevenueCat","description":"➡️ Fully customizable slide to unlock UI component for Jetpack Compose and KMP.","archived":false,"fork":false,"pushed_at":"2025-08-29T05:41:12.000Z","size":2063,"stargazers_count":87,"open_issues_count":1,"forks_count":8,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-08-29T09:35:14.285Z","etag":null,"topics":["android","compose","jetpack-compose","kmp","kotlin-multiplatform","library","revenuecat","sdk"],"latest_commit_sha":null,"homepage":"https://revenuecat.github.io/slide-to-unlock/","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/RevenueCat.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-08-22T01:35:48.000Z","updated_at":"2025-08-29T07:38:31.000Z","dependencies_parsed_at":"2025-08-22T04:09:28.747Z","dependency_job_id":"52cb4f28-54db-45ea-a38c-f809e4d73ee3","html_url":"https://github.com/RevenueCat/slide-to-unlock","commit_stats":null,"previous_names":["revenuecat/slide-to-unlock"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/RevenueCat/slide-to-unlock","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RevenueCat%2Fslide-to-unlock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RevenueCat%2Fslide-to-unlock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RevenueCat%2Fslide-to-unlock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RevenueCat%2Fslide-to-unlock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RevenueCat","download_url":"https://codeload.github.com/RevenueCat/slide-to-unlock/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RevenueCat%2Fslide-to-unlock/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":272988928,"owners_count":25026962,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-31T02:00:09.071Z","response_time":79,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","compose","jetpack-compose","kmp","kotlin-multiplatform","library","revenuecat","sdk"],"created_at":"2025-08-31T14:13:40.830Z","updated_at":"2025-08-31T14:13:41.605Z","avatar_url":"https://github.com/RevenueCat.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ca href=\"https://www.revenuecat.com/docs/getting-started/quickstart?utm_medium=organic\u0026utm_source=github\u0026utm_campaign=advocate\"\u003e\u003cimg src=\"previews/cover.png\" /\u003e\u003c/a\u003e\u003cbr\u003e\u003cbr\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://opensource.org/licenses/Apache-2.0\"\u003e\u003cimg alt=\"License\" src=\"https://img.shields.io/badge/License-Apache%202.0-blue.svg\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://android-arsenal.com/api?level=24\"\u003e\u003cimg alt=\"API\" src=\"https://img.shields.io/badge/API-24%2B-brightgreen.svg?style=flat\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/revenuecat/slide-to-unlock/actions/workflows/android.yml\"\u003e\u003cimg alt=\"Build Status\" src=\"https://github.com/revenuecat/slide-to-unlock/actions/workflows/android.yml/badge.svg\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://revenuecat.github.io/slide-to-unlock/\"\u003e\u003cimg alt=\"Build Status\" src=\"previews/dokka.svg\"/\u003e\u003c/a\u003e  \n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n➡️ Fully customizable slide to unlock UI component for Jetpack Compose and KMP.\n\u003c/p\u003e\u003cbr\u003e\n\n## 📷 Previews\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"previews/0.gif\" alt=\"drawing\" width=\"270px\" /\u003e\n\u003cimg src=\"previews/1.png\" alt=\"drawing\" width=\"270px\" /\u003e\n\u003cimg src=\"previews/2.png\" alt=\"drawing\" width=\"270px\" /\u003e\n\u003c/p\u003e\n\n\u003ca href=\"https://www.revenuecat.com/docs/getting-started/quickstart?utm_medium=organic\u0026utm_source=github\u0026utm_campaign=advocate\"\u003e\n\u003cimg src=\"https://github.com/user-attachments/assets/28d587ad-787a-4f67-9e33-2768a631c6e5\" align=\"right\" width=\"12%\"/\u003e\n\u003c/a\u003e\n\n## Contribution 🧡\nSlide to Unlock is maintained by [RevenueCat](https://www.revenuecat.com?utm_medium=organic\u0026utm_source=github\u0026utm_campaign=advocate). [RevenueCat SDK for Android](https://www.revenuecat.com/docs/getting-started/installation/android?utm_medium=organic\u0026utm_source=github\u0026utm_campaign=advocate) allows you to implement in-app subscriptions and a paywall system on top of Google Play Billing. Also, anyone can contribute to improving code, docs, or something following our [Contributing Guideline](https://github.com/RevenueCat/slide-to-unlock/blob/main/CONTRIBUTING.md).\n\n## Download\n[![Maven Central](https://img.shields.io/maven-central/v/com.revenuecat.purchases/slide-to-unlock.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22com.revenuecat%22%20AND%20a:%22slidetounlock%22)\n\n### Version Catalog\n\nIf you're using Version Catalog, you can configure the dependency by adding it to your `libs.versions.toml` file as follows:\n\n```toml\n[versions]\n#...\nslidetounlock = \"1.0.2\"\n\n[libraries]\n#...\ncompose-slidetounlock = { module = \"com.revenuecat.purchases:slide-to-unlock\", version.ref = \"slidetounlock\" }\n```\n\n### Gradle\n\nAdd the dependency below to your **module**'s `build.gradle.kts` file:\n\n```gradle\ndependencies {\n    implementation(\"com.revenuecat.purchases:slide-to-unlock:$version\")\n    \n    // if you're using Version Catalog\n    implementation(libs.compose.slidetounlock)\n}\n```\n\nFor Kotlin Multiplatform, add the dependency below to your **module**'s `build.gradle.kts` file:\n\n```gradle\nsourceSets {\n    val commonMain by getting {\n        dependencies {\n            implementation(libs.compose.slidetounlock)\n        }\n    }\n}\n```\n\n## Usage\n\nYou can easily implement a slide-to-unlock feature using the `SlideToUnlock` composable. It offers intuitive customization options for colors, text, shapes, and even the entire content of the thumb and hint, allowing you to create a wide variety of styles. If you want to just directly dive into the sample codes, check out the [demo project](https://github.com/RevenueCat/slide-to-unlock/tree/main/app).\n\n### Basic Usage\n\nThe `SlideToUnlock` composable exposes relevant state parameters, allowing you to hoist the slide status and track when the slide action is completed via a callback.\n\n- `isSlided`: A `Boolean` state that controls the component's state. When `true`, the thumb moves to the end and becomes disabled.\n- `onSlideCompleted`: A lambda that is invoked when the user successfully slides the thumb to the end. You should typically use this to update your `isSlided` state.\n\n```kotlin\nvar isSlided by remember { mutableStateOf(false) }\n\nSlideToUnlock(\n    isSlided = isSlided,\n    modifier = Modifier.fillMaxWidth(),\n    onSlideCompleted = { isSlided = true },\n)\n```\n\n### Customizing Colors\n\nYou can customize all colors of the component by leveraging an instance of `DefaultSlideToUnlockColors`, which contains some prebuilt behaviors for providing proper color sets depending on the states, and you can pass it to the `colors` parameter. This allows you to style the track, hint text, thumb, and progress indicator to match your app's theme.\n\n```kotlin\nvar isSlided by remember { mutableStateOf(false) }\n\nSlideToUnlock(\n    isSlided = isSlided,\n    modifier = Modifier\n        .fillMaxWidth()\n        .padding(16.dp),\n    colors = DefaultSlideToUnlockColors(\n        startTrackColor = Color.DarkGray,\n        endTrackColor = Color(0xFF11D483), // A green color\n        thumbColor = Color.White,\n        slidedHintColor = Color.White,\n    ),\n    onSlideCompleted = { isSlided = true },\n)\n```\n\nYou can also provide a `Brush` for the track background for more advanced gradient effects.\n\n```kotlin\nval colorStops = arrayOf(\n    0.0f to Color.Black,\n    1f to Color(0xDC393636),\n)\n\nSlideToUnlock(\n    isSlided = isSlided,\n    // ...\n    colors = DefaultSlideToUnlockColors(\n        trackBrush = Brush.verticalGradient(colorStops = colorStops),\n        // ...\n    ),\n    onSlideCompleted = { isSlided = true },\n)\n```\n\nWith `DefaultSlideToUnlockColors`, you can customize the properties below:\n\n* **startTrackColor**: The color of the track when the thumb is at the start.\n* **endTrackColor**: The color of the track when the thumb is at the end.\n* **trackBrush**: An optional brush for the track, which overrides color if non-null.\n* **startHintColor**: The color of the hint text at the start of the swipe.\n* **endHintColor**: The color of the hint text at the end of the swipe (usually transparent).\n* **slidedHintColor**: The color of the hint text after the slide is completed.\n* **thumbColor**: The solid background color of the thumb.\n* **thumbIconColor**: The color of the icon displayed inside the thumb.\n* **thumbBrush**: An optional brush for the thumb.\n* **progressColor**: The color used for the circular progress indicator.\n\nWhile `DefaultSlideToUnlockColors` offers a convenient way to set static and interpolated colors, you can achieve complete, dynamic control over the component's appearance by creating your own class that implements the `SlideToUnlockColors` interface.\n\nFor example, you can create your own color sets that awares Material theme color scheme:\n\n```kotlin\n@Stable\nprivate class MaterialThemedSlideToUnlockColors : SlideToUnlockColors {\n\n    @Composable\n    override fun trackColor(slideFraction: Float): Color {\n        // Linearly interpolate from the theme's surface color to the primary color.\n        return lerp(\n            start = MaterialTheme.colorScheme.surfaceVariant,\n            stop = MaterialTheme.colorScheme.primary,\n            fraction = (slideFraction / 0.85f).coerceIn(0f, 1f)\n        )\n    }\n\n    @Composable\n    override fun trackBrush(slideFraction: Float): Brush? = null\n\n    @Composable\n    override fun hintColor(slideFraction: Float): Color {\n        // Fade out the text color from onSurfaceVariant to transparent.\n        val startColor = MaterialTheme.colorScheme.onSurfaceVariant\n        return lerp(\n            start = startColor,\n            stop = startColor.copy(alpha = 0f),\n            fraction = (slideFraction / 0.45f).coerceIn(0f, 1f)\n        )\n    }\n\n    @Composable\n    override fun slidedHintColor(): Color {\n        return MaterialTheme.colorScheme.onPrimary\n    }\n\n    @Composable\n    override fun thumbColor(): Color {\n        return MaterialTheme.colorScheme.onPrimary\n    }\n\n    @Composable\n    override fun thumbBrush(slideFraction: Float): Brush?  = null\n\n    @Composable\n    override fun thumbIconColor(): Color {\n        return MaterialTheme.colorScheme.primary\n    }\n\n    @Composable\n    override fun progressColor(): Color {\n        return MaterialTheme.colorScheme.onPrimary\n    }\n}\n```\n\n### Customizing Hint Texts\n\nThe text displayed in the track can be customized by passing a `HintTexts` object. This allows you to define different messages for the initial state and the final \"slided\" state.\n\n```kotlin\nvar isSlided by remember { mutableStateOf(false) }\n\nSlideToUnlock(\n    isSlided = isSlided,\n    modifier = Modifier\n        .fillMaxWidth()\n        .padding(16.dp),\n    hintTexts = HintTexts(\n        defaultText = \"Slide to subscribe\",\n        slidedText = \"Subscribing...\",\n    ),\n    onSlideCompleted = { isSlided = true },\n)\n```\n\n### Fully Customize `thumb` and `hint` Composables\n\nFor complete control over the appearance and behavior of the thumb and hint, you can provide your own composable lambdas to the `thumb` and `hint` parameters. These are **slot APIs** that allow you to replace the default implementations entirely.\n\n#### Customizing the Thumb\n\nThe `thumb` slot provides you with the `isSlided` state, the current `slideFraction`, the `colors` object, and the `thumbSize`. You can use these to create dynamic and interactive thumbs.\n\nThis example replaces the default arrow icon with a rotating app icon and shows a success checkmark when completed.\n\n```kotlin\nvar isSlided by remember { mutableStateOf(false) }\nvar isCompleted by remember { mutableStateOf(false) }\n\nLaunchedEffect(isSlided) {\n    if (isSlided) {\n        delay(1500) // Simulate in-app purchases or any tasks\n        isCompleted = true\n    }\n}\n\nSlideToUnlock(\n    isSlided = isSlided,\n    // ...\n    onSlideCompleted = { isSlided = true },\n    thumb = { slided, fraction, colors, size -\u003e\n        Box(\n            modifier = Modifier\n                .size(size)\n                .background(color = colors.thumbColor(), shape = CircleShape),\n        ) {\n            if (isCompleted) {\n                Icon(\n                    imageVector = Icons.Default.Done,\n                    contentDescription = \"Completed\",\n                    tint = Color(0xFF11D483), // Green for success\n                )\n            } else if (slided) {\n                CircularProgressIndicator(\n                    modifier = Modifier.padding(8.dp),\n                    color = colors.progressColor(),\n                    strokeWidth = 3.dp,\n                )\n            } else {\n                Icon(\n                    modifier = Modifier\n                        .align(Alignment.Center)\n                        .size(30.dp)\n                        .rotate(fraction * -360), // Rotate the icon as the user slides\n                    imageVector = Icons.Default.Restore,\n                    tint = colors.thumbIconColor(),\n                    contentDescription = \"Slide to restore\",\n                )\n            }\n        }\n    },\n    // ...\n)\n```\n\n#### Customizing the Hint\n\nSimilarly, the `hint` composable slot gives you full control over the content displayed in the track. You can add animations, different text styles, or even other components like progress indicators.\n\nThis example replaces the default text with a `Row` that includes a `CircularProgressIndicator` when the slide is completed.\n\n```kotlin\nSlideToUnlock(\n    isSlided = isSlided,\n    // ...\n    onSlideCompleted = { isSlided = true },\n    hint = { slided, fraction, hintTexts, colors, paddings -\u003e\n        AnimatedContent(\n            modifier = Modifier.align(Alignment.Center),\n            targetState = isSlided,\n        ) { isSlidedTarget -\u003e\n            if (isSlidedTarget) {\n                Row(\n                    horizontalArrangement = Arrangement.Center,\n                    verticalAlignment = Alignment.CenterVertically,\n                ) {\n                    CircularProgressIndicator(\n                        modifier = Modifier.size(24.dp),\n                        color = Color.White,\n                        strokeWidth = 2.dp,\n                    )\n                    Text(\n                        modifier = Modifier.padding(start = 8.dp),\n                        text = hintTexts.slidedText,\n                        color = colors.slidedHintColor(),\n                        style = MaterialTheme.typography.titleMedium,\n                    )\n                }\n            } else {\n                Text(\n                    text = hintTexts.defaultText,\n                    color = colors.hintColor(fraction),\n                    style = MaterialTheme.typography.titleMedium,\n                )\n            }\n        }\n    },\n    // ...\n)\n```\n\n### Customizing the Gesture Behavior\n\nYou can customize the behavior and feel of the slide gesture using the `orientation` and `fractionalThreshold` parameters.\n\n#### Changing the Slide Orientation\n\nBy default, the component operates horizontally. You can change this to a vertical slide by setting the `orientation` parameter to `SlideOrientation.Vertical`.\n\nWhen using a vertical orientation, it's important to provide appropriate size constraints to the component's modifier, as it will orient itself along the y-axis.\n\n**Example: A Vertical Slider**\n\n```kotlin\nvar isSlided by remember { mutableStateOf(false) }\n\nSlideToUnlock(\n    isSlided = isSlided,\n    onSlideCompleted = { isSlided = true },\n    orientation = SlideOrientation.Vertical,\n    modifier = Modifier\n        .fillMaxHeight()\n        .width(80.dp) // A fixed width is recommended for vertical sliders\n        .padding(vertical = 32.dp)\n)\n```\n\n#### Adjusting the Slide Threshold\n\nThe `fractionalThreshold` parameter controls how far the user must drag the thumb before the slide action is considered complete upon release. It is a `Float` value between `0.0` and `1.0`, representing the percentage of the track that must be covered.\n\nThe default value is `0.85f` (85%), which requires a deliberate gesture. You can lower this value to make the slide easier to complete.\n\n**Example: An Easier-to-Complete Slider**\n\nThis slider will \"snap\" to the end and trigger `onSlideCompleted` if the user drags it just past the halfway point.\n\n```kotlin\nvar isSlided by remember { mutableStateOf(false) }\n\nSlideToUnlock(\n    isSlided = isSlided,\n    onSlideCompleted = { isSlided = true },\n    // The slide will complete if the user drags past the 50% mark.\n    fractionalThreshold = 0.5f,\n    modifier = Modifier\n        .fillMaxWidth()\n        .padding(16.dp)\n)\n```\n\n### Tracking Slide Fraction Changes\n\nIf you need to react to the slide progress in real-time, you can use the `onSlideFractionChanged` callback. This lambda is invoked continuously as the user drags the thumb, providing a `Float` value from `0.0` (start) to `1.0` (end).\n\nThis is useful for creating complex animations synchronized with the slide gesture.\n\n```kotlin\nvar slideProgress by remember { mutableFloatStateOf(0f) }\n\n// You can use `slideProgress` to animate other elements on the screen.\nText(\n    text = \"Progress: ${(slideProgress * 100).toInt()}%\",\n    modifier = Modifier.alpha(slideProgress)\n)\n\nSlideToUnlock(\n    isSlided = isSlided,\n    modifier = Modifier\n        .fillMaxWidth()\n        .padding(16.dp),\n    onSlideCompleted = { isSlided = true },\n    onSlideFractionChanged = { fraction -\u003e\n        slideProgress = fraction\n    }\n)\n```\n\n\u003ca href=\"https://www.revenuecat.com/docs/getting-started/installation/android?utm_medium=organic\u0026utm_source=github\u0026utm_campaign=advocate\"\u003e\n\u003cimg src=\"https://github.com/user-attachments/assets/28d587ad-787a-4f67-9e33-2768a631c6e5\" align=\"right\" width=\"12%\"/\u003e\n\u003c/a\u003e\n\n## RevenueCat Integration\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.revenuecat.purchases/slide-to-unlock.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22com.revenuecat%22%20AND%20a:%22slidetounlock%22)\n\nSlide to Unlock supports integrating features with [RevenueCat SDK](https://www.revenuecat.com/docs/getting-started/installation/kotlin-multiplatform?utm_medium=organic\u0026utm_source=github\u0026utm_campaign=advocate) for your Android \u0026 Kotlin Multiplatform project, and you can simply implement \"Slide to Purchases\" or \"Slide to Subscribe\" it by adding the dependency below:\n\n```toml\n[versions]\n#...\nslidetounlockPurchases = \"1.0.2\"\n\n[libraries]\n#...\ncompose-slidetounlock-purchases = { module = \"com.revenuecat.purchases:slide-to-unlock-purchases\", version.ref = \"slidetounlockPurchases\" }\n```\n\n### Gradle\n\nAdd the dependency below to your **module**'s `build.gradle.kts` file:\n\n```gradle\ndependencies {\n    implementation(\"com.revenuecat.purchases:slide-to-unlock-purchases:$version\")\n    \n    // if you're using Version Catalog\n    implementation(libs.compose.slidetounlock.purchases)\n}\n```\n\nFor Kotlin Multiplatform, add the dependency below to your **module**'s `build.gradle.kts` file:\n\n```gradle\nsourceSets {\n    val commonMain by getting {\n        dependencies {\n            implementation(libs.compose.slidetounlock.purchases)\n        }\n    }\n}\n```\n\n### SlideToPurchases for Jetpack Compose\n\nIt's very similar to use like `SlideToUnlock` composable function, but it contains a few more parameters as required to purchases in app product with RevenueCat SDK.\n\n`SlideToPurchases` is a customizable Jetpack Compose component that provides a sleek \"slide-to-unlock\" UI to initiate in-app purchases through the RevenueCat SDK. It's designed to be a drop-in replacement for a traditional purchase button, offering a more engaging and intentional user experience.\n\nThe library provides a set of overloaded `SlideToPurchases` composables to handle various purchasing scenarios, including different product types, subscription upgrades, and promotional offers, all while abstracting away the underlying purchase logic.\n\n### Expectation\n\n- **Engaging UI:** A \"slide-to-unlock\" gesture provides a clear and deliberate user action for making a purchase. You can highlight hint text and add dynamic behavior to boost user engagement.\n- **Seamless Integration:** Directly integrates with RevenueCat's `Purchases.sharedInstance.awaitPurchase` methods.\n- **State Handling:** Automatically manages and reports the state of the purchase (`Loading`, `Success`, `Error`) through a simple callback.\n- **Customizable:** Offers easy customization for colors and hint texts to match your app's branding.\n- **Flexible:** Basicailly supports all purchases methods by `StoreProduct`, `Package`, `SubscriptionOption`, and with `PromotionalOffer`s and `WinBackOffer`s.\n\nTo get started, simply add the `SlideToPurchases` composable to your UI. The primary entry points require you to provide a purchaseable entity, such as a `Package` or a `StoreProduct`.\n\n#### Basic Purchase with a `Package`\n\nThis is the most common use case. You fetch your offerings from RevenueCat and pass the desired `Package` to the composable. The component will handle the rest.\n\n```kotlin\n// inside your coroutine scope\nval currentOffering = Purchases.sharedInstance.awaitOfferings().current\nval monthlyPackage = currentOffering?.getPackage(\"monthly\")\n\nif (monthlyPackage != null) {\n    var purchaseState by remember { mutableStateOf\u003cPurchaseState?\u003e(null) }\n\n    SlideToPurchases(\n        packageToPurchase = monthlyPackage,\n        modifier = Modifier\n            .fillMaxWidth()\n            .padding(16.dp),\n        onPurchaseStateChanged = { newPurchaseState -\u003e\n            purchaseState = newPurchaseState\n            // Handle state changes, e.g., show a dialog on success/error\n        }\n    )\n\n    // Optionally display the state\n    when (val state = purchaseState) {\n        is PurchaseState.Loading -\u003e { /* Show a loading indicator */ }\n        is PurchaseState.Success -\u003e { /* Navigate to a success screen */ }\n        is PurchaseState.Error -\u003e { /* Show an error message */ }\n        null -\u003e { /* Initial state */ }\n    }\n}\n```\n\nThe `onPurchaseStateChanged` callback is the primary way to react to the outcome of the purchase flow. It provides a `PurchaseState` sealed class, which can be one of three types:\n\n-   `PurchaseState.Loading`: Triggered when the slide is completed and the purchase process begins.\n-   `PurchaseState.Success`: Triggered when the purchase is successful. It contains the `SuccessfulPurchase` object from the RevenueCat SDK.\n-   `PurchaseState.Error`: Triggered if the purchase fails or is cancelled by the user. It contains the `Exception`.\n\nThe `SlideToPurchases` composable is provided as a set of overloads, each tailored to a specific purchasing scenario.\n\n#### Common Parameters\n\nAll `SlideToPurchases` overloads share a set of common parameters for customization and event handling:\n\n-   `modifier: Modifier`: The `Modifier` to be applied to the component.\n-   `colors: SlideToUnlockColors`: An object to customize the component's appearance. Use `DefaultSlideToUnlockColors()` to get started.\n-   `hintTexts: HintTexts`: An object to customize the text displayed in the track. Use `HintTexts.defaultHintTexts()` for default values.\n-   `onSlideCompleted: () -\u003e Unit`: A callback invoked immediately when the user completes the slide gesture, right before the purchase flow is initiated.\n-   `onPurchaseStateChanged: (PurchaseState) -\u003e Unit`: A callback that receives updates on the state of the purchase operation.\n\n#### Overloads\n\n##### 1. Purchasing a `StoreProduct`\n\nUse this overload for purchasing a standalone `StoreProduct`. For subscriptions on Google Play, you can also manage upgrades.\n\n```kotlin\n@Composable\nfun SlideToPurchases(\n    storeProduct: StoreProduct,\n    // ... common and platform-specific parameters\n)\n```\n-  `oldProductId: String?`: (Google Play only) The product ID of a subscription to upgrade from.\n-  `replacementMode: GoogleReplacementMode`: (Google Play only) The proration mode for the upgrade.\n\n##### 2. Purchasing a `Package`\n\nThis is the recommended approach when using RevenueCat's Offerings system.\n\n```kotlin\n@Composable\nfun SlideToPurchases(\n    packageToPurchase: Package,\n    // ... common and platform-specific parameters\n)\n```\n\n##### 3. Purchasing a `SubscriptionOption`\n\nA Google Play-only overload for purchasing a specific subscription option, such as a particular plan and offer.\n\n```kotlin\n@Composable\nfun SlideToPurchases(\n    subscriptionOption: SubscriptionOption,\n    // ... common and platform-specific parameters\n)\n```\n\n##### 4. Purchasing with a `PromotionalOffer`\n\nAn App Store-only overload for applying a signed promotional offer to a purchase.\n\n```kotlin\n@Composable\nfun SlideToPurchases(\n    packageToPurchase: Package, // or storeProduct: StoreProduct\n    promotionalOffer: PromotionalOffer,\n    // ... common parameters\n)\n```\n\n##### 5. Purchasing with a `WinBackOffer`\n\nAn iOS-only overload (requires iOS 18+) for applying a win-back offer to a lapsed subscription.\n\n```kotlin\n@Composable\nfun SlideToPurchases(\n    packageToPurchase: Package, // or storeProduct: StoreProduct\n    winBackOffer: WinBackOffer,\n    // ... common parameters\n)\n```\n\nBy providing these clear and distinct overloads, `SlideToPurchaces` simplifies the process of integrating a beautiful and functional purchase UI into your Jetpack Compose app, allowing you to focus on your app's core logic while providing a great user experience.\n\n## Find this repository useful? 😻\n\nSupport it by joining __[stargazers](https://github.com/revenuecat/slide-to-unlock/stargazers)__ for this repository. :star: \u003cbr\u003e\nAlso, __[follow the main contributor](https://github.com/skydoves)__ on GitHub for the next creations! 🤩\n\n# License\n```xml\nCopyright (c) 2025 RevenueCat, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frevenuecat%2Fslide-to-unlock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frevenuecat%2Fslide-to-unlock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frevenuecat%2Fslide-to-unlock/lists"}