Create A Custom Vertical Slider In Compose for Android

In this post I will show you how I created a vertical slider in Compose for Android.

The root element is still the Slider Composable but we will just rotate it using the graphicsLayer Modifier.

This sample is taken from one of my projects and could be polished to fit your needs. I changed a bit of the parent elements and the location of it to fit in the center of the screen in either orientation, vertical or horizontal.

Upon implementation, you may need to adjust the offsets depending on how and where you place it.

During the change of the Slider position, I created a simple dot and Text to follow the location of the slider.

I created variables to hold the size of the slider for my use case. You may not need this either. You could create a fixed size.

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            SampleVerticalSliderTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    Box(
                        modifier = Modifier
                            .fillMaxSize()
                            .background(Color.Black)
                            .padding(innerPadding)
                    ) {
                        VerticalSlider(modifier = Modifier.align(Alignment.Center))
                    }
                }
            }
        }
    }
}

@Composable
fun VerticalSlider(modifier: Modifier = Modifier) {
    var sliderPosition by remember {
        mutableStateOf(50f)
    }

    val interactionSource = remember {
        MutableInteractionSource()
    }

    val isPressed by interactionSource.collectIsDraggedAsState()

    var currentConfig = LocalConfiguration.current
    var sliderHeight by remember { mutableStateOf(0.dp) }
    var sliderWidth by remember { mutableStateOf(0.dp) }
    val screenWidth = currentConfig.screenWidthDp
    var screenHeight = currentConfig.screenHeightDp
    var density = LocalDensity.current

    val size =
        if (screenWidth < screenHeight) 1.0f else (screenHeight.toFloat() / screenWidth.toFloat() - 0.1f)
    Slider(
        modifier = modifier
            .fillMaxWidth(size)
            .onGloballyPositioned { coordinates ->
                sliderHeight = with(density) { coordinates.size.height.toDp() }
                sliderWidth = with(density) { coordinates.size.width.toDp() }
            }
            .graphicsLayer {
                rotationZ = 270f
            },
        value = sliderPosition,
        onValueChange = {
            sliderPosition = it
        },
        valueRange = 0f..100f,
        interactionSource = interactionSource,
    )

    if (isPressed) {
        Box(modifier = Modifier
            .fillMaxSize()
            .offset(
                x = sliderHeight,
                y = sliderWidth / 2 - (sliderWidth * (sliderPosition / 100))
            )
            .drawBehind {
                drawCircle(
                    brush = Brush.radialGradient(
                        0.0f to Color.Green,
                        0.25f to Color.White,
                        1.0f to Color.Black.copy(alpha = 0.25f)
                    ),
                    center = center,
                    radius = sliderPosition
                )
            }) {
            Text(
                modifier = Modifier
                    .offset(
                        x = sliderHeight,
                        y = sliderHeight * (sliderPosition / 100)
                    )
                    .align(Alignment.Center),
                text = "$sliderPosition", color = Color.White, fontSize = 25.sp
            )
        }
    }
}


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *