Slider & Stepper in Compose: Numeric Input UI Patterns
Building numeric input interfaces in Jetpack Compose requires more than simple TextField components. Sliders and steppers provide intuitive, touch-friendly controls for users to adjust values. Let's explore the key patterns for modern Material 3 apps.
1. Basic Slider for Continuous Values
The Slider composable lets users drag to select any value in a range:
var sliderValue by remember { mutableStateOf(50f) }
Slider(
value = sliderValue,
onValueChange = { sliderValue = it },
valueRange = 0f..100f,
modifier = Modifier.padding(16.dp)
)
Text("Value: $sliderValue")
2. Stepped Slider for Discrete Values
For volume, brightness, or rating controls, enforce discrete steps:
var steppedValue by remember { mutableStateOf(5f) }
Slider(
value = steppedValue,
onValueChange = { steppedValue = it },
valueRange = 0f..10f,
steps = 9, // Creates 10 discrete positions (0, 1, 2, ..., 10)
modifier = Modifier.padding(16.dp)
)
The steps parameter splits the range into equal intervals. A value of 9 creates 10 discrete positions.
3. RangeSlider for Price/Duration Selection
Material 3 provides RangeSlider for selecting a minimum and maximum:
var priceRange by remember { mutableStateOf(100f..500f) }
RangeSlider(
value = priceRange,
onValueChange = { priceRange = it },
valueRange = 0f..1000f,
modifier = Modifier.padding(16.dp)
)
Text("Price: \$${priceRange.start.toInt()} - \$${priceRange.endInclusive.toInt()}")
Perfect for filtering products by price or time range in schedules.
4. Stepper: +/- Button Pattern
For precise numeric input, a custom Stepper with increment/decrement buttons:
@Composable
fun NumericStepper(
value: Int,
onValueChange: (Int) -> Unit,
min: Int = 0,
max: Int = 100
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(16.dp)
) {
Button(
onClick = { if (value > min) onValueChange(value - 1) }
) {
Text("−")
}
Text(
text = value.toString(),
modifier = Modifier.padding(horizontal = 16.dp)
)
Button(
onClick = { if (value < max) onValueChange(value + 1) }
) {
Text("+")
}
}
}
// Usage
var quantity by remember { mutableStateOf(1) }
NumericStepper(quantity, { quantity = it }, max = 10)
5. Customizing Slider Colors & Appearance
SliderDefaults.colors() lets you match your app's color scheme:
Slider(
value = sliderValue,
onValueChange = { sliderValue = it },
valueRange = 0f..100f,
colors = SliderDefaults.colors(
thumbColor = MaterialTheme.colorScheme.primary,
activeTrackColor = MaterialTheme.colorScheme.primaryContainer,
inactiveTrackColor = MaterialTheme.colorScheme.surfaceVariant
),
modifier = Modifier
.padding(16.dp)
.fillMaxWidth()
)
6. Labeled Slider with Visual Feedback
Combine slider with labels and dynamic text:
@Composable
fun LabeledSlider(
label: String,
value: Float,
onValueChange: (Float) -> Unit,
valueRange: ClosedFloatingPointRange<Float>
) {
Column(modifier = Modifier.padding(16.dp)) {
Text(label, style = MaterialTheme.typography.labelMedium)
Slider(
value = value,
onValueChange = onValueChange,
valueRange = valueRange,
modifier = Modifier.fillMaxWidth()
)
Text("${value.toInt()}", style = MaterialTheme.typography.bodySmall)
}
}
Best Practices
- Use Slider for continuous ranges (brightness, zoom level)
- Use RangeSlider for filtering (price, date range)
- Use Stepper for precise adjustment (quantity in cart, countdown timer)
- Add labels and feedback for clarity on what values mean
- Test touch targets - buttons should be 48+ dp for accessibility
-
Consider haptic feedback with
performHapticFeedback()for premium UX
Numeric inputs don't need to be boring. Sliders and steppers make value selection intuitive and delightful.
8 Android App Templates → https://myougatheaxo.gumroad.com
Top comments (0)