Customizable Fling Physics for Jetpack Compose
Take full control of scroll momentum in LazyColumn, LazyRow, Pagers, and more.
| Feature | Description |
|---|---|
| 9+ Built-in Presets | iOS-style, bouncy, floaty, snappy, ultra-smooth, and more |
| Full Physics Control | Customize friction, gravity, tension, deceleration curves |
| Snap-to-Item | Carousel/gallery snap behavior with smooth fusion |
| Adaptive Fling | Velocity-aware physics that adapts to swipe intensity |
| Accessibility | System-aware presets respecting "Reduce Motion" |
| Debug Overlay | Real-time fling visualization for development |
| All Lazy Layouts | Works with LazyColumn, LazyRow, Grids, and Pagers |
demo-video-2.mp4
// Just add flingBehavior to any scrollable Composable
LazyColumn(
flingBehavior = flingBehavior() // That's it!
) {
items(100) { Text("Item $it") }
}Or use a preset:
LazyColumn(flingBehavior = FlingPresets.iOSStyle()) // iOS-like scroll
LazyColumn(flingBehavior = FlingPresets.bouncy()) // Playful bounce
LazyColumn(flingBehavior = FlingPresets.floaty()) // Long glide- Installation
- Why Flinger?
- Usage Examples
- Configuration Parameters
- Preset Behaviors
- Advanced Features
- Sample App & Demos
- Compatibility
- Migration Guide
- Contributing
- Roadmap
- Community & Support
- License
- Privacy
| Document | Description |
|---|---|
| Physics Tuning Guide | Deep dive into all physics parameters with visual diagrams and recipes |
| Migration Guide | Upgrade from 1.x to 2.0 |
| Contributing | How to contribute to Flinger |
Step 1: Add JitPack repository to your settings.gradle.kts:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
}Step 2: Add the dependency to your module's build.gradle.kts:
dependencies {
implementation("com.github.iamjosephmj:flinger:2.0.2")
}Step 1: Add JitPack repository to your root build.gradle:
allprojects {
repositories {
google()
mavenCentral()
maven { url 'https://jitpack.io' }
}
}Step 2: Add the dependency:
dependencies {
implementation 'com.github.iamjosephmj:flinger:2.0.2'
}Add to your libs.versions.toml:
[versions]
flinger = "2.0.2"
[libraries]
flinger = { module = "com.github.iamjosephmj:flinger", version.ref = "flinger" }Then in your build.gradle.kts:
dependencies {
implementation(libs.flinger)
}Android's default fling behavior uses fixed physics that can't be customized. Flinger gives you complete control over scroll momentum, letting you create unique, polished scroll experiences.
| Default Compose Fling | With Flinger |
|---|---|
| Fixed scroll physics | Fully customizable |
| One-size-fits-all | Platform-specific feel (iOS, custom) |
| No control over deceleration | Fine-tune friction, gravity, tension |
| Hard to create unique UX | Create signature scroll experiences |
- Cross-platform apps - Match iOS scroll physics for consistency
- Photo galleries - Create buttery-smooth, long-glide scrolling
- Text-heavy lists - Build snappy, precise interactions with quick stops
- Branded experiences - Design unique scroll behaviors that match your app's personality
- Games & creative apps - Implement physics-based scrolling effects
import io.iamjosephmj.flinger.flings.flingBehavior
LazyColumn(
flingBehavior = flingBehavior()
) {
items(100) { index ->
ListItem(text = "Item $index")
}
}Flinger includes several pre-configured behaviors for common use cases:
import io.iamjosephmj.flinger.behaviours.FlingPresets
// Smooth, balanced scrolling (default)
LazyColumn(flingBehavior = FlingPresets.smooth())
// iOS-style scrolling with higher friction
LazyColumn(flingBehavior = FlingPresets.iOSStyle())
// Modified spline for unique feel
LazyColumn(flingBehavior = FlingPresets.smoothCurve())
// Quick deceleration for precision
LazyColumn(flingBehavior = FlingPresets.quickStop())
// Bouncy, playful feel
LazyColumn(flingBehavior = FlingPresets.bouncy())
// Long, floaty scrolls for galleries
LazyColumn(flingBehavior = FlingPresets.floaty())
// Snappy, responsive feel
LazyColumn(flingBehavior = FlingPresets.snappy())
// Ultra-smooth for premium apps
LazyColumn(flingBehavior = FlingPresets.ultraSmooth())
// Native Android behavior (for comparison)
LazyColumn(flingBehavior = FlingPresets.androidNative())Fine-tune fling physics with the configuration builder:
import io.iamjosephmj.flinger.configs.FlingConfiguration
import io.iamjosephmj.flinger.flings.flingBehavior
LazyColumn(
flingBehavior = flingBehavior(
scrollConfiguration = FlingConfiguration.Builder()
.scrollViewFriction(0.008f) // Lower = longer scroll
.decelerationFriction(0.09f) // Higher = quicker stop
.splineInflection(0.1f) // Higher = bouncier
.build()
)
) {
items(100) { index ->
ListItem(text = "Item $index")
}
}π‘ Tip: See the Physics Tuning Guide for detailed explanations of all parameters and ready-to-use recipes for common scenarios like iOS-style, photo galleries, and snappy UIs.
Flinger works seamlessly with all Compose scrollable components:
// LazyRow
LazyRow(flingBehavior = flingBehavior()) {
items(50) { HorizontalCard(it) }
}
// LazyVerticalGrid
LazyVerticalGrid(
columns = GridCells.Fixed(2),
flingBehavior = flingBehavior()
) {
items(100) { GridItem(it) }
}
// LazyHorizontalGrid
LazyHorizontalGrid(
rows = GridCells.Fixed(3),
flingBehavior = flingBehavior()
) {
items(100) { GridItem(it) }
}
// LazyVerticalStaggeredGrid
LazyVerticalStaggeredGrid(
columns = StaggeredGridCells.Fixed(2),
flingBehavior = flingBehavior()
) {
items(100) { StaggeredItem(it) }
}Change fling behavior based on state:
@Composable
fun DynamicFlingList(isPrecisionMode: Boolean) {
val flingConfig = remember(isPrecisionMode) {
if (isPrecisionMode) {
// Quick stop for precise selection
FlingConfiguration.Builder()
.decelerationFriction(0.5f)
.scrollViewFriction(0.04f)
.build()
} else {
// Smooth, long scrolls
FlingConfiguration.Builder()
.decelerationFriction(0.015f)
.scrollViewFriction(0.008f)
.build()
}
}
LazyColumn(
flingBehavior = flingBehavior(scrollConfiguration = flingConfig)
) {
items(100) { ListItem(it) }
}
}The most commonly adjusted parameters:
| Parameter | Default | What It Does |
|---|---|---|
scrollViewFriction |
0.008 |
Primary friction control. Lower = longer scroll distance |
decelerationFriction |
0.09 |
Stopping behavior. Higher = quicker stop |
splineInflection |
0.1 |
Curve shape. Higher = bouncier feel |
All parameters at a glance:
| Parameter | Default | Quick Description |
|---|---|---|
scrollViewFriction |
0.008 |
Resistance during scrolling |
decelerationFriction |
0.09 |
Friction during deceleration |
splineInflection |
0.1 |
Momentum curve transition point |
splineStartTension |
0.1 |
Launch feel (initial momentum) |
splineEndTension |
1.0 |
Landing feel (final momentum) |
decelerationRate |
2.358 |
Overall deceleration speed |
gravitationalForce |
9.8 |
Simulated gravity |
absVelocityThreshold |
0 |
Minimum velocity to trigger fling |
numberOfSplinePoints |
100 |
Animation smoothness |
π Want to master these parameters? See the Physics Tuning Guide for detailed explanations, visual diagrams, and tuning recipes.
Flinger includes pre-configured presets for common scenarios:
| Preset | Best For | Feel |
|---|---|---|
smooth() |
General purpose | Balanced, natural |
iOSStyle() |
Cross-platform apps | iOS-like, controlled |
quickStop() |
Text & reading | Precise, quick stop |
floaty() |
Photo galleries | Long, gliding scrolls |
bouncy() |
Games & playful UIs | Bouncy, responsive |
snappy() |
E-commerce | Quick, responsive |
ultraSmooth() |
Premium apps | Buttery smooth |
smoothCurve() |
Creative apps | Unique curve feel |
androidNative() |
Comparison | Stock Android |
object MyFlingPresets {
@Composable
fun galleryScroll() = flingBehavior(
scrollConfiguration = FlingConfiguration.Builder()
.scrollViewFriction(0.006f) // Long, floaty scrolls
.decelerationFriction(0.02f) // Smooth deceleration
.build()
)
@Composable
fun precisionList() = flingBehavior(
scrollConfiguration = FlingConfiguration.Builder()
.scrollViewFriction(0.05f) // Shorter scrolls
.decelerationFriction(0.3f) // Quick stop
.absVelocityThreshold(30f) // Ignore tiny swipes
.build()
)
}π More recipes in the Physics Tuning Guide β includes iOS-style, bouncy, ultra-premium, and accessibility configurations.
Create carousel-like experiences where items snap into place:
import io.iamjosephmj.flinger.snap.snapFlingBehavior
import io.iamjosephmj.flinger.snap.SnapPosition
val listState = rememberLazyListState()
LazyRow(
state = listState,
flingBehavior = snapFlingBehavior(
lazyListState = listState,
snapPosition = SnapPosition.Center, // Snap to center, start, or end
smoothFusion = true, // Smooth transition into snap
fusionVelocityRatio = 0.15f // When to start blending
)
) {
items(20) { PhotoCard(it) }
}Automatically adjust physics based on fling velocity:
import io.iamjosephmj.flinger.adaptive.adaptiveFlingBehavior
import io.iamjosephmj.flinger.adaptive.AdaptiveMode
LazyColumn(
flingBehavior = adaptiveFlingBehavior(
mode = AdaptiveMode.Balanced // or Precise, Momentum
)
) {
items(100) { ListItem(it) }
}Respect system accessibility settings:
import io.iamjosephmj.flinger.behaviours.FlingPresets
// Automatically adapts to system "Reduce Motion" setting
LazyColumn(flingBehavior = FlingPresets.accessibilityAware())
// Explicitly reduced motion
LazyColumn(flingBehavior = FlingPresets.reducedMotion())Monitor fling lifecycle events:
import io.iamjosephmj.flinger.callbacks.FlingCallbacks
val callbacks = FlingCallbacks(
onFlingStart = { velocity -> println("Fling started: $velocity") },
onFlingEnd = { println("Fling ended") }
)
LazyColumn(
flingBehavior = flingBehavior(callbacks = callbacks)
)Experience Flinger's capabilities firsthand with our sample app:
- Interactive Playground - Adjust all parameters in real-time
- Preset Gallery - Compare all built-in presets side-by-side
- Native vs Flinger - See the difference between default and customized scrolling
- Snap Gallery Demo - Experience snap-to-item behavior
- Pager Demo - Custom pager physics
- Debug Overlay - Visualize fling physics in real-time
π― Snap Behavior
demo-video-3.mp4
β‘ Side-by-Side Comparison
demo-video-4.mp4
π Custom Physics
demo-video-5.mp4
π¨ Pager Demo
demo-video-6.mp4
β¨ Debug Overlay
demo-video-7.mp4
| Component | Minimum Version | Recommended |
|---|---|---|
| Android API | 21 (Lollipop) | 26+ (Oreo) |
| Kotlin | 1.9.0 | 2.0+ |
| Compose BOM | 2024.01.00 | 2025.04.00 |
| Compose Compiler | 1.5.0 | 2.0+ |
| Android Gradle Plugin | 8.0.0 | 8.13.0+ |
- Jetpack Compose Material & Material3
- Accompanist libraries
- Compose Navigation
- All standard Lazy layouts
Version 2.0 includes breaking changes to improve API consistency and performance. See MIGRATION.md for detailed instructions.
Quick Summary:
// OLD (1.x) - Deprecated and removed
import io.iamjosephmj.flinger.bahaviours.StockFlingBehaviours
StockFlingBehaviours.smoothScroll()
StockFlingBehaviours.presetOne()
StockFlingBehaviours.presetTwo()
// NEW (2.0) - Use FlingPresets
import io.iamjosephmj.flinger.behaviours.FlingPresets
FlingPresets.smooth()
FlingPresets.iOSStyle()
FlingPresets.smoothCurve()// Core fling behavior
import io.iamjosephmj.flinger.flings.flingBehavior
// Configuration builder
import io.iamjosephmj.flinger.configs.FlingConfiguration
// Preset behaviors (NEW in 2.0)
import io.iamjosephmj.flinger.behaviours.FlingPresets
// Snap behavior
import io.iamjosephmj.flinger.snap.snapFlingBehavior
import io.iamjosephmj.flinger.snap.SnapPosition
// Adaptive behavior
import io.iamjosephmj.flinger.adaptive.adaptiveFlingBehaviorWe love contributions! Whether it's bug fixes, new features, or documentation improvements, your help is welcome.
- Fork the repository
- Clone your fork:
git clone https://github.com/YOUR_USERNAME/flinger.git - Create a branch:
git checkout -b feature/amazing-feature - Make your changes
- Test your changes:
./gradlew test - Commit:
git commit -m 'Add amazing feature' - Push:
git push origin feature/amazing-feature - Open a Pull Request to
developbranch
- Ensure all tests pass before submitting PR
- Follow existing code style and conventions
- Add tests for new functionality
- Update documentation as needed
- Run Android Studio lint analyzer
Have a great fling configuration? Add it to FlingPresets.kt! Include:
- Descriptive function name
- KDoc explaining the use case
- Recommended scenarios
See CONTRIBUTING.md for detailed guidelines.
- Custom fling configuration
- Multiple preset behaviors
- Support for all Lazy layouts
- Sample application
- Snap-to-item fling behavior
- Velocity-aware adaptive fling
- Fling event callbacks
- Accessibility presets
- Pager integration
- Debug visualization overlay
- Physics tuning documentation
- Fix Debug Overlay
- Kotlin DSL builder syntax
- Enhanced documentation site
- Compose Multiplatform support
- More real-world presets (Material You)
- Performance profiling tools
- Bug Reports: GitHub Issues
- Feature Requests: GitHub Issues
- Discussions: GitHub Discussions
- Twitter: @iamjosephmj
- GitHub: @iamjosephmj
MIT License
Copyright (c) 2021 Joseph James
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Flinger is privacy-friendly and GDPR compliant:
- β No data collection
- β No analytics or tracking
- β No internet permissions required
- β 100% offline - all processing happens locally on device
- β No personal data processed
This library only handles scroll physics calculations in-memory and does not collect, store, or transmit any user information.
- Inspired by Android's native
OverScroller - Built for the Jetpack Compose community
- Thanks to all contributors
If Flinger helps your project, please star the repo!
Made with β€οΈ by Joseph James
