{"id":19521791,"url":"https://github.com/smarttoolfactory/compose-beforeafter","last_synced_at":"2025-10-06T13:43:42.723Z","repository":{"id":47897983,"uuid":"516386845","full_name":"SmartToolFactory/Compose-BeforeAfter","owner":"SmartToolFactory","description":"🚀🌆🏙 Display differences or animate progress between 2 images or Composables with overlay and customization options, zoom, pan gestures, and progress to observe properties for animating before-after progress","archived":false,"fork":false,"pushed_at":"2025-09-25T08:13:08.000Z","size":11328,"stargazers_count":196,"open_issues_count":1,"forks_count":12,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-09-28T22:30:57.344Z","etag":null,"topics":["android","before-after","jetpack-compose","jetpack-compose-canvas","material-design","material-design-3"],"latest_commit_sha":null,"homepage":"","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/SmartToolFactory.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-07-21T13:39:42.000Z","updated_at":"2025-09-25T08:13:05.000Z","dependencies_parsed_at":"2024-09-10T09:04:53.985Z","dependency_job_id":"f2fba882-c2c2-4437-a61f-e0a9667580a4","html_url":"https://github.com/SmartToolFactory/Compose-BeforeAfter","commit_stats":{"total_commits":96,"total_committers":4,"mean_commits":24.0,"dds":0.5520833333333333,"last_synced_commit":"e7d2abac6b867b45bb318253423c1c2b7f4f4139"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/SmartToolFactory/Compose-BeforeAfter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SmartToolFactory%2FCompose-BeforeAfter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SmartToolFactory%2FCompose-BeforeAfter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SmartToolFactory%2FCompose-BeforeAfter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SmartToolFactory%2FCompose-BeforeAfter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SmartToolFactory","download_url":"https://codeload.github.com/SmartToolFactory/Compose-BeforeAfter/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SmartToolFactory%2FCompose-BeforeAfter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278621844,"owners_count":26017253,"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-10-06T02:00:05.630Z","response_time":65,"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","before-after","jetpack-compose","jetpack-compose-canvas","material-design","material-design-3"],"created_at":"2024-11-11T00:34:56.096Z","updated_at":"2025-10-06T13:43:42.718Z","avatar_url":"https://github.com/SmartToolFactory.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Compose Before-After\n\n[![](https://jitpack.io/v/SmartToolFactory/Compose-BeforeAfter.svg)](https://jitpack.io/#SmartToolFactory/Compose-BeforeAfter)\n\nComposables to display Images, or Composables as before and after composables to display\ndifferences or animate progress between 2 layouts or Composables with overlay and\ncustomization options and progress observe properties for animating before-after progress\n\nhttps://github.com/user-attachments/assets/60a3b755-046f-4683-b3eb-cd89fb728d64\n\n## Gradle Setup\n\nTo get a Git project into your build:\n\n**Step 1:** Add the JitPack repository to your build file Add it in your root build.gradle at the end of repositories:\n\n```gradle\nallprojects {\n  repositories {\n      ...\n      maven { url 'https://jitpack.io' }\n  }\n}\n```\n\n**Step 2:** Add the dependency\n\n```gradle\ndependencies {\n    implementation 'com.github.SmartToolFactory:Compose-BeforeAfter:\u003cversion\u003e'\n}\n```\n\nwhere `\u003cversion\u003e` is one available in [![](https://www.jitpack.io/v/SmartToolFactory/Compose-BeforeAfter.svg)](https://www.jitpack.io/#SmartToolFactory/Compose-BeforeAfter)\n\n## BeforeAfterImage\n\nImage that takes two `ImageBitmaps as parameter and displays them based on specified order.\nBy default Labels and Overlay is provided and overload function that accept other Composables\nas overlay to customize\n\n```kotlin\n@Composable\nfun BeforeAfterImage(\n    modifier: Modifier = Modifier,\n    beforeImage: ImageBitmap,\n    afterImage: ImageBitmap,\n    enableProgressWithTouch: Boolean = true,\n    enableZoom: Boolean = true,\n    contentOrder: ContentOrder = ContentOrder.BeforeAfter,\n    overlayStyle: OverlayStyle = OverlayStyle(),\n    beforeLabel: @Composable BoxScope.() -\u003e Unit = { BeforeLabel(contentOrder = contentOrder) },\n    afterLabel: @Composable BoxScope.() -\u003e Unit = { AfterLabel(contentOrder = contentOrder) },\n    contentScale: ContentScale = ContentScale.Fit,\n    alignment: Alignment = Alignment.Center,\n    contentDescription: String? = null,\n) {\n}\n```\n\nand\n\n```kotlin\n@Composable\nfun BeforeAfterImage(\n    modifier: Modifier = Modifier,\n    beforeImage: ImageBitmap,\n    afterImage: ImageBitmap,\n    enableProgressWithTouch: Boolean = true,\n    enableZoom: Boolean = true,\n    contentOrder: ContentOrder = ContentOrder.BeforeAfter,\n    @FloatRange(from = 0.0, to = 100.0) progress: Float = 50f,\n    onProgressChange: ((progress: Float) -\u003e Unit)? = null,\n    overlayStyle: OverlayStyle = OverlayStyle(),\n    beforeLabel: @Composable BoxScope.() -\u003e Unit = { BeforeLabel(contentOrder = contentOrder) },\n    afterLabel: @Composable BoxScope.() -\u003e Unit = { AfterLabel(contentOrder = contentOrder) },\n    contentScale: ContentScale = ContentScale.Fit,\n    alignment: Alignment = Alignment.Center,\n    contentDescription: String? = null,\n)\n```\n\noverloads has default `DefaultOverlay`\n\n```kotlin\n@Composable\ninternal fun DefaultOverlay(\n    width: Dp,\n    height: Dp,\n    position: Offset,\n    overlayStyle: OverlayStyle\n)\n```\n\n```kotlin\n@Composable\nfun BeforeAfterImage(\n    modifier: Modifier = Modifier,\n    beforeImage: ImageBitmap,\n    afterImage: ImageBitmap,\n    enableProgressWithTouch: Boolean = true,\n    enableZoom: Boolean = true,\n    contentOrder: ContentOrder = ContentOrder.BeforeAfter,\n    alignment: Alignment = Alignment.Center,\n    contentScale: ContentScale = ContentScale.Fit,\n    contentDescription: String? = null,\n    alpha: Float = DefaultAlpha,\n    colorFilter: ColorFilter? = null,\n    filterQuality: FilterQuality = DrawScope.DefaultFilterQuality,\n    beforeLabel: @Composable BoxScope.() -\u003e Unit = { BeforeLabel(contentOrder = contentOrder) },\n    afterLabel: @Composable BoxScope.() -\u003e Unit = { AfterLabel(contentOrder = contentOrder) },\n    overlay: @Composable BeforeAfterImageScope.() -\u003e Unit\n)\n```\n\nand\n\n```kotlin\n@Composable\nfun BeforeAfterImage(\n    modifier: Modifier = Modifier,\n    beforeImage: ImageBitmap,\n    afterImage: ImageBitmap,\n    enableProgressWithTouch: Boolean = true,\n    enableZoom: Boolean = true,\n    contentOrder: ContentOrder = ContentOrder.BeforeAfter,\n    @FloatRange(from = 0.0, to = 100.0) progress: Float = 50f,\n    onProgressChange: ((progress: Float) -\u003e Unit)? = null,\n    alignment: Alignment = Alignment.Center,\n    contentScale: ContentScale = ContentScale.Fit,\n    contentDescription: String? = null,\n    alpha: Float = DefaultAlpha,\n    colorFilter: ColorFilter? = null,\n    filterQuality: FilterQuality = DrawScope.DefaultFilterQuality,\n    beforeLabel: @Composable BoxScope.() -\u003e Unit = { BeforeLabel(contentOrder = contentOrder) },\n    afterLabel: @Composable BoxScope.() -\u003e Unit = { AfterLabel(contentOrder = contentOrder) },\n    overlay: @Composable BeforeAfterImageScope.() -\u003e Unit\n)\n```\n\nhas `overlay`parameters to add another Composable to draw overlay\n\n### Usage\n\n```kotlin\nBeforeAfterImage(\n    modifier = Modifier\n        .clip(RoundedCornerShape(10.dp))\n        .fillMaxWidth()\n        .aspectRatio(4 / 3f),\n    beforeImage = imageBefore,\n    afterImage = imageAfter,\n    contentScale = contentScale\n)\n```\n\nor\n\n```kotlin\nBeforeAfterImage(\n    modifier = Modifier\n        .clip(RoundedCornerShape(10.dp))\n        .border(3.dp, Color(0xffE91E63), RoundedCornerShape(10.dp))\n        .fillMaxWidth()\n        .aspectRatio(4 / 3f),\n    beforeImage = imageBefore3,\n    afterImage = imageAfter3,\n    progress = progress,\n    onProgressChange = {},\n    contentScale = contentScale,\n)\n```\n\n### Parameters\n\n-   **beforeImage** image that show initial progress\n-   **afterImage** image that show final progress\n-   **enableProgressWithTouch** flag to enable drag and change progress with touch\n-   **enableZoom** when enabled images are zoomable and pannable\n-   **contentOrder** order of images to be drawn\n-   **alignment** determines where image will be aligned inside `BoxWithConstraints`\n-   **contentScale** how image should be scaled inside Canvas to match parent dimensions.\n-   `ContentScale.Fit` for instance maintains src ratio and scales image to fit inside the parent.\n-   **alpha** Opacity to be applied to `beforeImage` from 0.0f to 1.0f representing fully transparent to fully opaque respectively\n-   **colorFilter** ColorFilter to apply to the `beforeImage` when drawn into the destination\n-   **filterQuality** Sampling algorithm applied to the `beforeImage` when it is scaled and drawn into the destination. The default is `FilterQuality.Low` which scales using a bilinear sampling algorithm\n-   **overlay** is a Composable that can be matched at exact position where `beforeImage` is drawn. This is useful for drawing thumbs, cropping or another layout that should match position with the image that is scaled is drawn\n\n## BeforeAfterLayout\n\nLayout can draw any Composable, image, or video as before and after layout and can be zoomed and\npanned when [enableZoom] is **true** and returns a callback to animate before/after progress\n\n```kotlin\n@Composable\nfun BeforeAfterLayout(\n    modifier: Modifier = Modifier,\n    enableProgressWithTouch: Boolean = true,\n    enableZoom: Boolean = true,\n    contentOrder: ContentOrder = ContentOrder.BeforeAfter,\n    overlayStyle: OverlayStyle = OverlayStyle(),\n    beforeContent: @Composable () -\u003e Unit,\n    afterContent: @Composable () -\u003e Unit,\n    beforeLabel: @Composable BoxScope.() -\u003e Unit = { BeforeLabel(contentOrder = contentOrder) },\n    afterLabel: @Composable BoxScope.() -\u003e Unit = { AfterLabel(contentOrder = contentOrder) },\n)\n```\n\nand\n\n```kotlin\n@Composable\nfun BeforeAfterLayout(\n    modifier: Modifier = Modifier,\n    enableProgressWithTouch: Boolean = true,\n    enableZoom: Boolean = true,\n    contentOrder: ContentOrder = ContentOrder.BeforeAfter,\n    @FloatRange(from = 0.0, to = 100.0) progress: Float = 50f,\n    onProgressChange: ((progress: Float) -\u003e Unit)? = null,\n    overlayStyle: OverlayStyle = OverlayStyle(),\n    beforeContent: @Composable () -\u003e Unit,\n    afterContent: @Composable () -\u003e Unit,\n    beforeLabel: @Composable BoxScope.() -\u003e Unit = { BeforeLabel(contentOrder = contentOrder) },\n    afterLabel: @Composable BoxScope.() -\u003e Unit = { AfterLabel(contentOrder = contentOrder) },\n)\n```\n\n## Usage\n\n### Customize\n\n```kotlin\n        BeforeAfterLayout(\n    modifier = Modifier\n        .shadow(1.dp, RoundedCornerShape(10.dp))\n        .fillMaxWidth()\n        .aspectRatio(4 / 3f),\n    beforeContent = {\n        DemoImage(imageBitmap = imageBefore)\n    },\n    afterContent = {\n        DemoImage(imageBitmap = imageAfter)\n    },\n    overlayStyle = OverlayStyle(\n        dividerColor = Color(0xffF44336),\n        dividerWidth = 2.dp,\n        thumbShape = CutCornerShape(8.dp),\n        thumbBackgroundColor = Color.Red,\n        thumbTintColor = Color.White\n    )\n)\n```\n\n### Display difference between Composables with Material Design2 and M3\n\n```kotlin\n        BeforeAfterLayout(\n    modifier = Modifier\n        .padding(8.dp)\n        .fillMaxWidth(),\n    progress = progress,\n    beforeContent = {\n        BeforeComposable(progress)\n    },\n    afterContent = {\n        AfterComposable(progress)\n    },\n    enableProgressWithTouch = false,\n    enableZoom = false,\n    beforeLabel = null,\n    afterLabel = null,\n    overlay = null\n)\n```\n\n### Animate like a ProgressBar\n\n```kotlin\nval transition: InfiniteTransition = rememberInfiniteTransition()\n\n// Infinite progress animation\nval progress by transition.animateFloat(\n    initialValue = 0f,\n    targetValue = 100f,\n    animationSpec = infiniteRepeatable(\n        animation = tween(\n            durationMillis = 4000,\n            easing = FastOutSlowInEasing\n        ),\n        repeatMode = RepeatMode.Reverse\n    )\n)\n\nBeforeAfterLayout(\n    modifier = Modifier\n        .padding(8.dp)\n        .fillMaxWidth(),\n    progress = progress,\n    beforeContent = {\n        BeforeComposable(progress)\n    },\n    afterContent = {\n        AfterComposable(progress)\n    },\n    enableProgressWithTouch = false,\n    enableZoom = false,\n    beforeLabel = null,\n    afterLabel = null,\n    overlay = null\n)\n```\n\n### Display before and after videos with Exoplayer\n\nThere is `ExoPlayerUsingTextureView` composable that you can use to display before and after videos.\n\n```kotlin\nBeforeAfterLayout(\n    modifier = Modifier\n        .fillMaxSize()\n        .aspectRatio(4 / 3f),\n    beforeContent = {\n        ExoPlayerUsingTextureView(\n            uri = \"asset:///floodplain_dirty.mp4\"\n        )\n    },\n    afterContent = {\n        ExoPlayerUsingTextureView(\n            uri = \"asset:///floodplain_clean.mp4\"\n        )\n    },\n    enableZoom = false\n)\n```\n\n\u003e [!NOTE]  \n\u003e If you would like the ability to customize and build your own VideoPlayer composable using Exoplayer, then take a look at the implementation of `ExoPlayerUsingTextureView` composable. You can duplicate the composable and modify the behaviour.\n\u003e\n\u003e Only thing to take care of is that `BeforeAfterLayout` requires Exoplayer that works with a TextureView.\n\u003e Inside the `ExoPlayerUtil` is a public extension function named `ExoPlayer.createTextureView` that you should use with your ExoPlayer.\n\n## License\n\n[Apache License, Version 2.0](LICENSE.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmarttoolfactory%2Fcompose-beforeafter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsmarttoolfactory%2Fcompose-beforeafter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmarttoolfactory%2Fcompose-beforeafter/lists"}