State of the scroll. Allows the developer to change the scroll position or get current state by calling methods on this object. To be hosted and passed to Modifier.verticalScroll or Modifier.horizontalScroll

To create and automatically remember ScrollState with default parameters use rememberScrollState.

Learn how to control the state of Modifier.verticalScroll or Modifier.horizontalScroll:

import androidx.compose.foundation.gestures.animateScrollBy
import androidx.compose.foundation.gestures.scrollBy
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.Text
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier

// Create ScrollState to own it and be able to control scroll behaviour of scrollable Row below
val scrollState = rememberScrollState()
val scope = rememberCoroutineScope()
Column {
    Row(Modifier.horizontalScroll(scrollState)) { repeat(1000) { index -> Square(index) } }
    // Controls for scrolling
    Row(verticalAlignment = Alignment.CenterVertically) {
        Text("Scroll")
        Button(onClick = { scope.launch { scrollState.scrollTo(scrollState.value - 1000) } }) {
            Text("< -")
        }
        Button(onClick = { scope.launch { scrollState.scrollBy(10000f) } }) { Text("--- >") }
    }
    Row(verticalAlignment = Alignment.CenterVertically) {
        Text("Smooth Scroll")
        Button(
            onClick = { scope.launch { scrollState.animateScrollTo(scrollState.value - 1000) } }
        ) {
            Text("< -")
        }
        Button(onClick = { scope.launch { scrollState.animateScrollBy(10000f) } }) {
            Text("--- >")
        }
    }
}

Summary

Public companion properties

Saver<ScrollState, *>

The default Saver implementation for ScrollState.

Cmn

Public constructors

ScrollState(initial: Int)
Cmn

Public functions

suspend Unit
animateScrollTo(value: Int, animationSpec: AnimationSpec<Float>)

Scroll to position in pixels with animation.

Cmn
open Float

Dispatch scroll delta in pixels avoiding all scroll related mechanisms.

Cmn
open suspend Unit
scroll(scrollPriority: MutatePriority, block: suspend ScrollScope.() -> Unit)

Call this function to take control of scrolling and gain the ability to send scroll events via ScrollScope.scrollBy.

Cmn
suspend Float
scrollTo(value: Int)

Instantly jump to the given position in pixels.

Cmn

Public properties

open Boolean

Whether this ScrollableState can scroll backward (consume a negative delta).

Cmn
open Boolean

Whether this ScrollableState can scroll forward (consume a positive delta).

Cmn
InteractionSource

InteractionSource that will be used to dispatch drag events when this list is being dragged.

Cmn
open Boolean

Whether this ScrollableState is currently scrolling by gesture, fling or programmatically or not.

Cmn
open Boolean

The value of this property is true under the following scenarios, otherwise it's false.

Cmn
open Boolean

The value of this property is true under the following scenarios, otherwise it's false.

Cmn
Int

maximum bound for value, or Int.MAX_VALUE if still unknown

Cmn
Int

current scroll position value in pixels

Cmn
Int

Size of the viewport on the scrollable axis, or 0 if still unknown.

Cmn

Public companion properties

Saver

val SaverSaver<ScrollState, *>

The default Saver implementation for ScrollState.

Public constructors

ScrollState

ScrollState(initial: Int)
Parameters
initial: Int

value of the scroll

Public functions

animateScrollTo

suspend fun animateScrollTo(
    value: Int,
    animationSpec: AnimationSpec<Float> = SpringSpec()
): Unit

Scroll to position in pixels with animation.

Parameters
value: Int

target value in pixels to smooth scroll to, value will be coerced to 0..maxPosition

animationSpec: AnimationSpec<Float> = SpringSpec()

animation curve for smooth scroll animation

dispatchRawDelta

open fun dispatchRawDelta(delta: Float): Float

Dispatch scroll delta in pixels avoiding all scroll related mechanisms.

NOTE: unlike scroll, dispatching any delta with this method won't trigger nested scroll, won't stop ongoing scroll/drag animation and will bypass scrolling of any priority. This method will also ignore reverseDirection and other parameters set in scrollable.

This method is used internally for nested scrolling dispatch and other low level operations, allowing implementers of ScrollableState influence the consumption as suits them. Manually dispatching delta via this method will likely result in a bad user experience, you must prefer scroll method over this one.

Parameters
delta: Float

amount of scroll dispatched in the nested scroll process

Returns
Float

the amount of delta consumed

scroll

open suspend fun scroll(scrollPriority: MutatePriority, block: suspend ScrollScope.() -> Unit): Unit

Call this function to take control of scrolling and gain the ability to send scroll events via ScrollScope.scrollBy. All actions that change the logical scroll position must be performed within a scroll block (even if they don't call any other methods on this object) in order to guarantee that mutual exclusion is enforced.

If scroll is called from elsewhere with the scrollPriority higher or equal to ongoing scroll, ongoing scroll will be canceled.

scrollTo

suspend fun scrollTo(value: Int): Float

Instantly jump to the given position in pixels.

Cancels the currently running scroll, if any, and suspends until the cancellation is complete.

Parameters
value: Int

number of pixels to scroll by

Returns
Float

the amount of scroll consumed

See also
animateScrollTo

for an animated version

Public properties

canScrollBackward

open val canScrollBackwardBoolean

Whether this ScrollableState can scroll backward (consume a negative delta). This is typically false if the scroll position is equal to its minimum value, and true otherwise.

Note that true here does not imply that delta will be consumed - the ScrollableState may decide not to handle the incoming delta (such as if it is already being scrolled separately). Additionally, for backwards compatibility with previous versions of ScrollableState this value defaults to true.

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material.icons.filled.KeyboardArrowUp
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer

val state = rememberLazyListState()
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
    Icon(
        Icons.Filled.KeyboardArrowUp,
        null,
        Modifier.graphicsLayer {
            // Hide the icon if we cannot scroll backward (we are the start of the list)
            // We use graphicsLayer here to control the alpha so that we only redraw when this
            // value changes, instead of recomposing
            alpha = if (state.canScrollBackward) 1f else 0f
        },
        Color.Red
    )
    val items = (1..100).toList()
    LazyColumn(Modifier.weight(1f).fillMaxWidth(), state) {
        items(items) { Text("Item is $it") }
    }
    Icon(
        Icons.Filled.KeyboardArrowDown,
        null,
        Modifier.graphicsLayer {
            // Hide the icon if we cannot scroll forward (we are the end of the list)
            // We use graphicsLayer here to control the alpha so that we only redraw when this
            // value changes, instead of recomposing
            alpha = if (state.canScrollForward) 1f else 0f
        },
        Color.Red
    )
}

canScrollForward

open val canScrollForwardBoolean

Whether this ScrollableState can scroll forward (consume a positive delta). This is typically false if the scroll position is equal to its maximum value, and true otherwise.

Note that true here does not imply that delta will be consumed - the ScrollableState may decide not to handle the incoming delta (such as if it is already being scrolled separately). Additionally, for backwards compatibility with previous versions of ScrollableState this value defaults to true.

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material.icons.filled.KeyboardArrowUp
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer

val state = rememberLazyListState()
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
    Icon(
        Icons.Filled.KeyboardArrowUp,
        null,
        Modifier.graphicsLayer {
            // Hide the icon if we cannot scroll backward (we are the start of the list)
            // We use graphicsLayer here to control the alpha so that we only redraw when this
            // value changes, instead of recomposing
            alpha = if (state.canScrollBackward) 1f else 0f
        },
        Color.Red
    )
    val items = (1..100).toList()
    LazyColumn(Modifier.weight(1f).fillMaxWidth(), state) {
        items(items) { Text("Item is $it") }
    }
    Icon(
        Icons.Filled.KeyboardArrowDown,
        null,
        Modifier.graphicsLayer {
            // Hide the icon if we cannot scroll forward (we are the end of the list)
            // We use graphicsLayer here to control the alpha so that we only redraw when this
            // value changes, instead of recomposing
            alpha = if (state.canScrollForward) 1f else 0f
        },
        Color.Red
    )
}

interactionSource

val interactionSourceInteractionSource

InteractionSource that will be used to dispatch drag events when this list is being dragged. If you want to know whether the fling (or smooth scroll) is in progress, use isScrollInProgress.

isScrollInProgress

open val isScrollInProgressBoolean

Whether this ScrollableState is currently scrolling by gesture, fling or programmatically or not.

lastScrolledBackward

open val lastScrolledBackwardBoolean

The value of this property is true under the following scenarios, otherwise it's false.

lastScrolledForward

open val lastScrolledForwardBoolean

The value of this property is true under the following scenarios, otherwise it's false.

maxValue

val maxValueInt

maximum bound for value, or Int.MAX_VALUE if still unknown

value

val valueInt

current scroll position value in pixels

viewportSize

val viewportSizeInt

Size of the viewport on the scrollable axis, or 0 if still unknown. Note that this value is only populated after the first measure pass.