Compose Unstyled 2.0 is out! Check the official announcement blog ->

Scrollbars

Vertical and horizontal scrollbars for scroll states and lazy layouts.

Use scrollbars when content containers need clearer scroll affordances.

View on GitHub
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import com.composables.ui.components.Text
import com.composables.ui.components.VerticalScrollbar
import com.composables.ui.components.rememberVerticalScrollbarState
import com.composables.ui.theme.colors
import com.composables.ui.theme.controlColor
import com.composeunstyled.theme.Theme

@Composable
fun ScrollbarExample() {
    val scrollState = rememberScrollState()
    val scrollbarState = rememberVerticalScrollbarState(scrollState)
    Box(
        modifier = Modifier
            .height(220.dp)
            .widthIn(max = 340.dp)
            .clip(RoundedCornerShape(8.dp))
            .background(Theme[colors][controlColor]),
    ) {
        Column(
            modifier = Modifier
                .verticalScroll(scrollState)
                .padding(16.dp),
            verticalArrangement = Arrangement.spacedBy(10.dp),
        ) {
            repeat(16) { index ->
                Text("Scrollable row ${index + 1}")
            }
        }
        VerticalScrollbar(
            state = scrollbarState,
            modifier = Modifier
                .align(Alignment.CenterEnd)
                .fillMaxHeight(),
        )
    }
}

Installation

implementation("com.composables:ui:0.1.0")

Examples

Horizontal

View on GitHub
import androidx.compose.foundation.background
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import com.composables.ui.components.HorizontalScrollbar
import com.composables.ui.components.Text
import com.composables.ui.components.rememberVerticalScrollbarState
import com.composables.ui.theme.colors
import com.composables.ui.theme.controlColor
import com.composables.ui.theme.panelColor
import com.composeunstyled.theme.Theme

@Composable
fun HorizontalScrollbarExample() {
    val scrollState = rememberScrollState()
    val scrollbarState = rememberVerticalScrollbarState(scrollState)
    Box(
        modifier = Modifier
            .height(88.dp)
            .widthIn(max = 340.dp)
            .clip(RoundedCornerShape(8.dp))
            .background(Theme[colors][controlColor]),
    ) {
        Row(
            modifier = Modifier
                .horizontalScroll(scrollState)
                .padding(16.dp),
            horizontalArrangement = Arrangement.spacedBy(12.dp),
        ) {
            repeat(10) { index ->
                Box(
                    modifier = Modifier
                        .size(width = 96.dp, height = 40.dp)
                        .clip(RoundedCornerShape(6.dp))
                        .background(Theme[colors][panelColor]),
                    contentAlignment = Alignment.Center,
                ) {
                    Text("Item ${index + 1}")
                }
            }
        }
        HorizontalScrollbar(
            state = scrollbarState,
            modifier = Modifier
                .align(Alignment.BottomCenter)
                .fillMaxWidth(),
        )
    }
}

LazyColumn

View on GitHub
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import com.composables.ui.components.Text
import com.composables.ui.components.VerticalScrollbar
import com.composables.ui.components.rememberVerticalScrollbarState
import com.composables.ui.theme.colors
import com.composables.ui.theme.controlColor
import com.composeunstyled.theme.Theme

@Composable
fun LazyColumnScrollbarExample() {
    val lazyListState = rememberLazyListState()
    val scrollbarState = rememberVerticalScrollbarState(lazyListState)
    Box(
        modifier = Modifier
            .height(132.dp)
            .width(340.dp)
            .clip(RoundedCornerShape(8.dp))
            .background(Theme[colors][controlColor]),
    ) {
        LazyColumn(
            state = lazyListState,
            verticalArrangement = Arrangement.spacedBy(10.dp),
            contentPadding = PaddingValues(16.dp),
            modifier = Modifier.fillMaxWidth()
        ) {
            items(200) { index ->
                Text("Build artifact ${index + 1}")
            }
        }
        VerticalScrollbar(
            state = scrollbarState,
            modifier = Modifier
                .align(Alignment.CenterEnd)
                .fillMaxHeight(),
        )
    }
}

Always visible

View on GitHub
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import com.composables.ui.components.Text
import com.composables.ui.components.VerticalScrollbar
import com.composables.ui.components.rememberVerticalScrollbarState
import com.composables.ui.theme.colors
import com.composables.ui.theme.controlColor
import com.composeunstyled.theme.Theme

@Composable
fun AlwaysVisibleScrollbarExample() {
    val scrollState = rememberScrollState()
    val scrollbarState = rememberVerticalScrollbarState(scrollState)
    Box(
        modifier = Modifier
            .height(132.dp)
            .widthIn(max = 340.dp)
            .clip(RoundedCornerShape(8.dp))
            .background(Theme[colors][controlColor]),
    ) {
        Column(
            modifier = Modifier
                .verticalScroll(scrollState)
                .padding(16.dp),
            verticalArrangement = Arrangement.spacedBy(10.dp),
        ) {
            repeat(24) { index ->
                Text("Always visible row ${index + 1}")
            }
        }
        VerticalScrollbar(
            state = scrollbarState,
            autoHide = false,
            modifier = Modifier
                .align(Alignment.CenterEnd)
                .fillMaxHeight(),
        )
    }
}

Disabled

View on GitHub
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import com.composables.ui.components.Text
import com.composables.ui.components.VerticalScrollbar
import com.composables.ui.components.rememberVerticalScrollbarState
import com.composables.ui.theme.colors
import com.composables.ui.theme.controlColor
import com.composeunstyled.theme.Theme

@Composable
fun DisabledScrollbarExample() {
    val scrollState = rememberScrollState()
    val scrollbarState = rememberVerticalScrollbarState(scrollState)
    Box(
        modifier = Modifier
            .height(220.dp)
            .widthIn(max = 340.dp)
            .clip(RoundedCornerShape(8.dp))
            .background(Theme[colors][controlColor]),
    ) {
        Column(
            modifier = Modifier
                .verticalScroll(scrollState)
                .padding(16.dp),
            verticalArrangement = Arrangement.spacedBy(10.dp),
        ) {
            repeat(16) { index ->
                Text("Scrollable row ${index + 1}")
            }
        }
        VerticalScrollbar(
            state = scrollbarState,
            enabled = false,
            autoHide = false,
            modifier = Modifier
                .align(Alignment.CenterEnd)
                .fillMaxHeight(),
        )
    }
}

Horizontal disabled

View on GitHub
import androidx.compose.foundation.background
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import com.composables.ui.components.HorizontalScrollbar
import com.composables.ui.components.Text
import com.composables.ui.components.rememberVerticalScrollbarState
import com.composables.ui.theme.colors
import com.composables.ui.theme.controlColor
import com.composables.ui.theme.panelColor
import com.composeunstyled.theme.Theme

@Composable
fun DisabledHorizontalScrollbarExample() {
    val scrollState = rememberScrollState()
    val scrollbarState = rememberVerticalScrollbarState(scrollState)
    Box(
        modifier = Modifier
            .height(88.dp)
            .widthIn(max = 340.dp)
            .clip(RoundedCornerShape(8.dp))
            .background(Theme[colors][controlColor]),
    ) {
        Row(
            modifier = Modifier
                .horizontalScroll(scrollState)
                .padding(16.dp),
            horizontalArrangement = Arrangement.spacedBy(12.dp),
        ) {
            repeat(10) { index ->
                Box(
                    modifier = Modifier
                        .size(width = 96.dp, height = 40.dp)
                        .clip(RoundedCornerShape(6.dp))
                        .background(Theme[colors][panelColor]),
                    contentAlignment = Alignment.Center,
                ) {
                    Text("Item ${index + 1}")
                }
            }
        }
        HorizontalScrollbar(
            state = scrollbarState,
            enabled = false,
            modifier = Modifier
                .align(Alignment.BottomCenter)
                .fillMaxWidth(),
        )
    }
}

API Reference

rememberVerticalScrollbarState

Creates and remembers scrollbar state for a scrollable container.

@Composable
fun rememberVerticalScrollbarState(scrollState: ScrollState): ScrollbarState
Parameter Type Description
scrollState ScrollState ScrollState used to drive the scrollbar.

rememberVerticalScrollbarState

Creates and remembers scrollbar state for a scrollable container.

@Composable
fun rememberVerticalScrollbarState(lazyListState: LazyListState): ScrollbarState
Parameter Type Description
lazyListState LazyListState LazyListState used to drive the scrollbar.

rememberVerticalScrollbarState

Creates and remembers scrollbar state for a scrollable container.

@Composable
fun rememberVerticalScrollbarState(lazyGridState: LazyGridState): ScrollbarState
Parameter Type Description
lazyGridState LazyGridState LazyGridState used to drive the scrollbar.

VerticalScrollbar

A vertical scrollbar tied to a ScrollbarState.

@Composable
fun VerticalScrollbar(
    state: ScrollbarState,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    reverseLayout: Boolean = false,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    thumbColor: Color = Theme[colors][mutedColor],
    thumbShape: Shape = RoundedCornerShape(999.dp),
    autoHide: Boolean = true,
)
Parameter Type Description
state ScrollbarState Scrollbar state used by the scrollbar component.
modifier Modifier Modifier applied to the scrollbar.
enabled Boolean Whether the scrollbar can be interacted with.
reverseLayout Boolean Whether the underlying layout scroll direction is reversed.
interactionSource MutableInteractionSource Interaction source used for dragging and hover state.
thumbColor Color Color used for the scrollbar thumb.
thumbShape Shape Shape used for the scrollbar thumb.
autoHide Boolean Whether the scrollbar thumb fades out while idle.

HorizontalScrollbar

A horizontal scrollbar tied to a ScrollbarState.

@Composable
fun HorizontalScrollbar(
    state: ScrollbarState,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    reverseLayout: Boolean = false,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    thumbColor: Color = Theme[colors][mutedColor],
    thumbShape: Shape = RoundedCornerShape(999.dp),
    autoHide: Boolean = true,
)
Parameter Type Description
state ScrollbarState Scrollbar state used by the scrollbar component.
modifier Modifier Modifier applied to the scrollbar.
enabled Boolean Whether the scrollbar can be interacted with.
reverseLayout Boolean Whether the underlying layout scroll direction is reversed.
interactionSource MutableInteractionSource Interaction source used for dragging and hover state.
thumbColor Color Color used for the scrollbar thumb.
thumbShape Shape Shape used for the scrollbar thumb.
autoHide Boolean Whether the scrollbar thumb fades out while idle.