Skip to content

farsroidx/ComposeElasticOverscroll

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

19 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Due to the Iran-US war and the lack of access to some international services, we are currently unable to publish the version to the jitPack and mavenCentral repositories.

The versions will be published in the two repositories as soon as possible.

ElasticOverscroll Banner

🌟 ElasticOverscroll for Jetpack Compose 🌟

The ultimate, buttery-smooth iOS-like elastic overscroll effect for Android!
Built natively with Modifier.Node for maximum 60fps performance and zero unnecessary recompositions.

Min SDK Compose Codacy Badge License Last Commit

Android Kotlin Made with Love

Made with ❀️ for the Android Community.

Elastic Overscroll Demo
Elastic Overscroll Demo Elastic Overscroll Demo Elastic Overscroll Demo
Elastic Overscroll Demo Elastic Overscroll Demo Elastic Overscroll Demo

πŸš€ Why ElasticOverscroll?

  • ⚑ Insanely Fast: Uses Compose's DrawModifierNode to translate pixels directly on the canvas. Zero recomposition during the scroll!
  • 🧲 Advanced Physics: Implements an exponential resistance model $$(1 - progress)^2$$ so it feels like a real physical spring.
  • πŸ”’ Edge Locking: Prevent overscroll on specific edges (Full RTL support out of the box).
  • 🎣 Perfect for Pull-to-Refresh: Built-in onProgress and onReleased callbacks.

πŸ“¦ Installation

First, ensure you have the required repositories in your settings.gradle.kts (or build.gradle.kts project level):

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        maven { url = uri("https://jitpack.io") } // Add this if you use JitPack
    }
}

Then, add the dependency to your app-level build.gradle.kts:

Option A: Using Maven Central (Recommended) Maven Central

dependencies {
    implementation("ir.farsroidx:overscroll:1.0.0")
}

Option B: Using JitPack

dependencies {
    implementation("com.github.farsroidx:ElasticOverscroll:1.0.0")
}

πŸ› οΈ Usage & Modifiers

1. The Quick & Easy Way (For normal Columns/Rows)

If you have a standard layout that you want to make scrollable with elasticity:

Column(
    modifier = Modifier
        .fillMaxSize()
        .verticalElasticScrollable(
            maxStretchRatio = 30, // Stretches up to 30% of screen height
            springDampingRatio = 1f, // No wobble, crisp snap-back
        )
) {
    // Your content here
}

Row(
    modifier = Modifier
        .fillMaxSize()
        .horizontalElasticScrollable(
            maxStretchRatio = 30, // Stretches up to 30% of screen width
            springDampingRatio = 1f, // No wobble, crisp snap-back
        )
) {
    // Your content here
}

2. The LazyColumn / LazyRow Way (🚨 IMPORTANT)

For Compose Lazy lists, they already have their own scrolling logic. You must use the standard overscrollEffect and pass our remembered effect to it!

LazyColumn(
    modifier = Modifier.fillMaxSize(),
    overscrollEffect = rememberVerticalElasticOverscroll(
        maxStretchRatio = 20,
        springDampingRatio = 0.5F,
        lockedEdge = ElasticOverscrollEdgeLock.BOTTOM // Don't bounce at the bottom!
    )
) {
    items(50) { index ->
        Text("Item $index", modifier = Modifier.padding(16.dp))
    }
}

LazyRow(
    modifier = Modifier.fillMaxSize(),
    overscrollEffect = rememberHorizontalElasticOverscroll(
        maxStretchRatio = 20,
        springDampingRatio = 0.5F,
        lockedEdge = ElasticOverscrollEdgeLock.START // Don't bounce at the start!
    )
) {
    items(50) { index ->
        Text("Item $index", modifier = Modifier.padding(16.dp))
    }
}

🀯 Advanced Examples

πŸ”₯ Building a Custom Pull-to-Refresh

Want to trigger a refresh when the user pulls down enough? Use the onReleased and onProgress callbacks!

var pullProgress by remember { mutableFloatStateOf(0f) }

val overscrollEffect = rememberVerticalElasticOverscroll(
    maxStretchRatio = 25,
    lockedEdge = ElasticOverscrollEdgeLock.BOTTOM, // Only allow pull from the top
    onProgress = { progress ->
        pullProgress = progress // Values from 0.0 to 1.0
    },
    onReleased = { finalProgress ->
        if (finalProgress > 0.8f) {
            // Trigger your refresh action here!
            viewModel.refreshData()
        }
    }
)

Box(modifier = Modifier.fillMaxSize()) {

    LazyColumn(overscrollEffect = overscrollEffect) {
        // Items...
    }

    // Custom loading indicator that fades in based on pull progress
    if (pullProgress > 0f) {

        CircularProgressIndicator(
            progress = {
                pullProgress
            },
            modifier = Modifier.align(Alignment.TopCenter).padding(top = 16.dp),
            alpha = pullProgress
        )
    }
}

↔️ Horizontal Pager/Row with RTL Support

It works flawlessly for horizontal scrolls. If your user's device is in Arabic/Persian (RTL), START automatically adapts!

HorizontalPager(
    state = pagerState,
    modifier = Modifier.fillMaxSize(),
    overscrollEffect = rememberHorizontalElasticOverscroll(
        maxStretchRatio = 20,
        springDampingRatio = 0.5F,
        lockedEdge = ElasticOverscrollEdgeLock.START // Don't bounce at the start!
    )
) { index ->
    // Content
}

// .

Row(
    modifier = Modifier
        .fillMaxWidth()
        .horizontalElasticScrollable(
            maxStretchRatio = 15,
            lockedEdge = ElasticOverscrollEdgeLock.START // Locks the right edge in RTL, left in LTR!
        )
) {
    // Content
}

Note: Prevents drawing outside bounds horizontalElasticScrollableContainer()


βš™οΈ API Reference

Configuration Parameters

Parameter Type Default Description
maxStretchRatio Int 25 Maximum screen percentage the view can stretch ($1$ to $100$).
springDampingRatio Float 1f Bounciness of the return animation ($1f$ = critically damped/no wobble).
springStiffness Float Medium Speed/stiffness of the snap-back animation.
snapBackForce Float? null A multiplier to optionally increase the stiffness during touch release.
lockedEdge Enum null TOP, BOTTOM, START, END - Prevents stretching on a specific edge.
onProgress Callback null Emits progress from $0.0$ to $1.0$ while dragging or animating.
onReleased Callback null Emits exactly once when the user lifts their finger.

πŸ“œ License

Copyright 2026 Farsroidx

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Ask Me Anything!

About

A smooth elastic overscroll effect for Jetpack Compose lists, scroll and lazy layouts.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages