Skip to content

Customizable Fling Physics for Jetpack Compose. Take full control of scroll momentum in LazyColumn, LazyRow, Pagers, and more with 9+ presets, snap behavior, and adaptive physics.

License

Notifications You must be signed in to change notification settings

iamjosephmj/flinger

Repository files navigation

Flinger Banner

Customizable Fling Physics for Jetpack Compose
Take full control of scroll momentum in LazyColumn, LazyRow, Pagers, and more.

API 21+ Kotlin 2.0+ Compose BOM 2025 JitPack License MIT

GitHub Stars Android Arsenal jetc.dev Newsletter GDPR Compliant


✨ Key Features

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

🎬 See It In Action

demo-video-2.mp4

πŸš€ Quick Start

// 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

Table of Contents

Additional Documentation

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

Installation

Gradle (Kotlin DSL)

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")
}

Gradle (Groovy)

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'
}

Version Catalog

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)
}

Why 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

Use Cases

  • 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

Usage Examples

Basic Usage - Default Smooth Scroll

import io.iamjosephmj.flinger.flings.flingBehavior

LazyColumn(
    flingBehavior = flingBehavior()
) {
    items(100) { index ->
        ListItem(text = "Item $index")
    }
}

Using Preset Behaviors

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())

Custom Configuration

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.

Works With All Lazy Layouts

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) }
}

Dynamic Configuration

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) }
    }
}

Configuration Parameters

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.


Preset Behaviors

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

Creating Your Own Presets

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.


Advanced Features

Snap-to-Item Behavior

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) }
}

Adaptive Fling Behavior

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) }
}

Accessibility-Aware Presets

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())

Fling Callbacks

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)
)

Sample App & Demos

Experience Flinger's capabilities firsthand with our sample app:

Download Sample APK

App Features

  • 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

Demo Videos

🎯 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

Compatibility

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+

Tested With

  • Jetpack Compose Material & Material3
  • Accompanist libraries
  • Compose Navigation
  • All standard Lazy layouts

Migration Guide

From 1.x to 2.0

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()

Common Imports

// 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.adaptiveFlingBehavior

Contributing

We love contributions! Whether it's bug fixes, new features, or documentation improvements, your help is welcome.

Quick Start

  1. Fork the repository
  2. Clone your fork: git clone https://github.com/YOUR_USERNAME/flinger.git
  3. Create a branch: git checkout -b feature/amazing-feature
  4. Make your changes
  5. Test your changes: ./gradlew test
  6. Commit: git commit -m 'Add amazing feature'
  7. Push: git push origin feature/amazing-feature
  8. Open a Pull Request to develop branch

Guidelines

  • 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

Contributing Presets

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.


Roadmap

Completed

  • 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

In Progress

  • Fix Debug Overlay
  • Kotlin DSL builder syntax
  • Enhanced documentation site

Planned

  • Compose Multiplatform support
  • More real-world presets (Material You)
  • Performance profiling tools

Community & Support

Get Help

Connect

Star History

Star History Chart

License

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.

Privacy

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.


Acknowledgements


If Flinger helps your project, please star the repo!

GitHub Stars

Made with ❀️ by Joseph James

About

Customizable Fling Physics for Jetpack Compose. Take full control of scroll momentum in LazyColumn, LazyRow, Pagers, and more with 9+ presets, snap behavior, and adaptive physics.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •  

Languages