Compose Component

VerticalSlider

Material Design slider Vertical Sliders allow users to make selections from a range of values.

VerticalSlider social preview

VerticalSliderSample

@Preview
@Sampled
@Composable
fun VerticalSliderSample() {
    val coroutineScope = rememberCoroutineScope()
    val sliderState =
        rememberSliderState(
            // Only allow multiples of 10. Excluding the endpoints of `valueRange`,
            // there are 9 steps (10, 20, ..., 90).
            steps = 9,
            trackRange = 0f..100f,
        )
    val snapAnimationSpec = MaterialTheme.motionScheme.fastEffectsSpec<Float>()
    var currentValue by rememberSaveable { mutableFloatStateOf(sliderState.value) }
    var animateJob: Job? by remember { mutableStateOf(null) }
    sliderState.shouldAutoSnap = false
    val interactionSource = remember { MutableInteractionSource() }
    Column(modifier = Modifier.padding(horizontal = 16.dp)) {
        Text(
            modifier = Modifier.align(Alignment.CenterHorizontally),
            text = "%.2f".format(sliderState.value),
        )
        Spacer(Modifier.height(16.dp))
        VerticalSlider(
            state = sliderState,
            modifier =
                Modifier.height(300.dp)
                    .align(Alignment.CenterHorizontally)
                    .progressSemantics(
                        currentValue,
                        sliderState.trackRange.start..sliderState.trackRange.endInclusive,
                        sliderState.steps,
                    ),
            onValueChange = { newValue ->
                currentValue = newValue
                // only update the sliderState instantly if dragging
                if (sliderState.isDragging) {
                    animateJob?.cancel()
                    sliderState.value = newValue
                }
            },
            onValueChangeFinished = {
                animateJob = coroutineScope.launch {
                    animate(
                        initialValue = sliderState.value,
                        targetValue = currentValue,
                        animationSpec = snapAnimationSpec,
                    ) { value, _ ->
                        sliderState.value = value
                    }
                }
            },
            interactionSource = interactionSource,
            track = {
                SliderDefaults.Track(
                    sliderState = sliderState,
                    modifier = Modifier.width(36.dp),
                    trackCornerSize = 12.dp,
                )
            },
            topToBottom = false,
        )
    }
}

VerticalCenteredSliderSample

@Preview
@Sampled
@Composable
fun VerticalCenteredSliderSample() {
    val coroutineScope = rememberCoroutineScope()
    val sliderState =
        rememberSliderState(
            // Only allow multiples of 10. Excluding the endpoints of `valueRange`,
            // there are 9 steps (10, 20, ..., 90).
            steps = 9,
            trackRange = -50f..50f,
        )
    val snapAnimationSpec = MaterialTheme.motionScheme.fastEffectsSpec<Float>()
    var currentValue by rememberSaveable { mutableFloatStateOf(sliderState.value) }
    var animateJob: Job? by remember { mutableStateOf(null) }
    sliderState.shouldAutoSnap = false
    val interactionSource = remember { MutableInteractionSource() }
    Column(modifier = Modifier.padding(horizontal = 16.dp)) {
        Text(
            modifier = Modifier.align(Alignment.CenterHorizontally),
            text = "%.2f".format(sliderState.value),
        )
        Spacer(Modifier.height(16.dp))
        VerticalSlider(
            state = sliderState,
            modifier =
                Modifier.height(300.dp)
                    .align(Alignment.CenterHorizontally)
                    .progressSemantics(
                        currentValue,
                        sliderState.trackRange.start..sliderState.trackRange.endInclusive,
                        sliderState.steps,
                    ),
            onValueChange = { newValue ->
                currentValue = newValue
                // only update the sliderState instantly if dragging
                if (sliderState.isDragging) {
                    animateJob?.cancel()
                    sliderState.value = newValue
                }
            },
            onValueChangeFinished = {
                animateJob = coroutineScope.launch {
                    animate(
                        initialValue = sliderState.value,
                        targetValue = currentValue,
                        animationSpec = snapAnimationSpec,
                    ) { value, _ ->
                        sliderState.value = value
                    }
                }
            },
            interactionSource = interactionSource,
            track = { SliderDefaults.CenteredTrack(sliderState = sliderState) },
            topToBottom = false,
        )
    }
}

Content updated: