What You Will Build

A screen with two independently controlled sliders: a primary slider using default Material3 styling and a secondary slider with custom orange theming. Both display their current values as large text labels above the track, and a summary card below shows the precise values. This pattern extends to price range filters, audio equalizers, and any dual-parameter control.

Why This Pattern Matters

While Material3 provides a built-in RangeSlider composable, understanding how to compose multiple individual sliders with different color schemes gives you the flexibility to build any multi-slider configuration. This is essential for settings screens and filter UIs.

The Dual Slider Screen

@Composable
fun RangeSliderScreen() {
    var value by remember { mutableFloatStateOf(0.5f) }
    var value2 by remember { mutableFloatStateOf(0.3f) }

    Column(
        modifier = Modifier.fillMaxSize().padding(24.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text("Range Slider",
            style = MaterialTheme.typography.headlineSmall,
            fontWeight = FontWeight.Bold)
        Spacer(Modifier.height(40.dp))

        // Primary value display and slider
        Text("${'$'}{(value * 100).toInt()}%",
            fontSize = 48.sp, fontWeight = FontWeight.Bold,
            color = MaterialTheme.colorScheme.primary)
        Spacer(Modifier.height(16.dp))
        Slider(value = value, onValueChange = { value = it })

        Spacer(Modifier.height(24.dp))

        // Secondary slider with custom colors
        Text("Secondary: ${'$'}{(value2 * 100).toInt()}%",
            fontSize = 20.sp, fontWeight = FontWeight.Medium)
        Slider(
            value = value2,
            onValueChange = { value2 = it },
            colors = SliderDefaults.colors(
                thumbColor = Color(0xFFFF9800),
                activeTrackColor = Color(0xFFFF9800)
            )
        )

        Spacer(Modifier.height(32.dp))

        // Summary card
        Card(modifier = Modifier.fillMaxWidth()) {
            Column(Modifier.padding(16.dp)) {
                Text("Value 1: ${'$'}{String.format("%.2f", value)}",
                    style = MaterialTheme.typography.bodyLarge)
                Text("Value 2: ${'$'}{String.format("%.2f", value2)}",
                    style = MaterialTheme.typography.bodyLarge)
            }
        }
    }
}

Using the Material3 RangeSlider

For a true two-thumb range slider, Compose Material3 offers RangeSlider:

@Composable
fun PriceRangeFilter() {
    var range by remember { mutableStateOf(20f..80f) }

    Column(modifier = Modifier.padding(24.dp)) {
        Text("Price: ${'$'}${'$'}{range.start.toInt()} - ${'$'}${'$'}{range.endInclusive.toInt()}")
        RangeSlider(
            value = range,
            onValueChange = { range = it },
            valueRange = 0f..200f,
            steps = 19
        )
    }
}

Tips and Pitfalls

  • RangeSlider takes a ClosedFloatingPointRange<Float> state value, not two separate floats.
  • steps parameter creates discrete snap points between the start and end. 19 steps on a 0-200 range snaps every 10 units.
  • Custom colors via SliderDefaults.colors() apply to both thumbColor and activeTrackColor for visual consistency.
  • For price filters: Format the display as currency and debounce the onValueChange to avoid excessive API calls during drag.

Get New Tutorials by Email

No spam. Just clear, practical breakdowns you can apply right away.

Enjoy this tutorial?

Get new practical tech tutorials in your inbox.