Lumo UI provides Slider and RangeSlider components for selecting values from a continuous or discrete range.
Import
import com.nomanr.lumo.ui.components.Slider
import com.nomanr.lumo.ui.components.RangeSlider
import com.nomanr.lumo.ui.components.SliderState
import com.nomanr.lumo.ui.components.RangeSliderState
Slider
A slider for selecting a single value from a range.
Component Signature
@Composable
fun Slider(
value: Float,
onValueChange: (Float) -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
onValueChangeFinished: (() -> Unit)? = null,
colors: SliderColors = SliderDefaults.colors(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
@IntRange(from = 0) steps: Int = 0,
valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
)
Source: Slider.kt:38-70
State-based Slider
@Composable
fun Slider(
state: SliderState,
modifier: Modifier = Modifier,
enabled: Boolean = true,
colors: SliderColors = SliderDefaults.colors(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
)
Source: Slider.kt:73-83
RangeSlider
A slider for selecting a range of values (start and end).
Component Signature
@Composable
fun RangeSlider(
value: ClosedFloatingPointRange<Float>,
onValueChange: (ClosedFloatingPointRange<Float>) -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
@IntRange(from = 0) steps: Int = 0,
onValueChangeFinished: (() -> Unit)? = null,
colors: SliderColors = SliderDefaults.colors(),
startInteractionSource: MutableInteractionSource = remember { MutableInteractionSource() },
endInteractionSource: MutableInteractionSource = remember { MutableInteractionSource() },
)
Source: Slider.kt:86-122
Parameters
Slider Parameters
Current value of the slider
Callback invoked when the value changes during dragging
modifier
Modifier
default:"Modifier"
Modifier to be applied to the slider
Whether the slider is enabled and can be interacted with
onValueChangeFinished
(() -> Unit)?
default:"null"
Callback invoked when the user finishes adjusting the slider (on release)
valueRange
ClosedFloatingPointRange<Float>
default:"0f..1f"
Range of values the slider can take
Number of discrete steps between min and max. 0 means continuous
colors
SliderColors
default:"SliderDefaults.colors()"
Colors for different slider states and parts
RangeSlider Parameters
value
ClosedFloatingPointRange<Float>
required
Current range (start..end) of the slider
onValueChange
(ClosedFloatingPointRange<Float>) -> Unit
required
Callback invoked when either start or end value changes
Interaction source for the start thumb
Interaction source for the end thumb
Examples
Basic Slider
From preview (Slider.kt:191-202):
var value by remember { mutableFloatStateOf(0.5f) }
Slider(
value = value,
onValueChange = { value = it },
modifier = Modifier.fillMaxWidth()
)
Stepped Slider
From preview (Slider.kt:204-216):
var value by remember { mutableFloatStateOf(0.4f) }
Column {
Text("Stepped Slider (5 steps)", style = AppTheme.typography.h4)
Slider(
value = value,
onValueChange = { value = it },
steps = 4, // 4 steps creates 5 positions: 0, 0.25, 0.5, 0.75, 1
modifier = Modifier.fillMaxWidth()
)
}
Custom Range Slider
From preview (Slider.kt:218-241):
var value by remember { mutableFloatStateOf(30f) }
Column {
Text("Custom Range (0-100)", style = AppTheme.typography.h4)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Slider(
value = value,
onValueChange = { value = it },
valueRange = 0f..100f,
modifier = Modifier.weight(1f)
)
Text(
text = "${value.toInt()}",
style = AppTheme.typography.body1,
modifier = Modifier.width(40.dp)
)
}
}
Disabled Slider
From preview (Slider.kt:243-261):
Column {
Text("Disabled States", style = AppTheme.typography.h4)
Slider(
value = 0.3f,
onValueChange = {},
enabled = false,
modifier = Modifier.fillMaxWidth()
)
Spacer(Modifier.height(8.dp))
Slider(
value = 0.7f,
onValueChange = {},
enabled = false,
modifier = Modifier.fillMaxWidth()
)
}
Interactive Slider with Callbacks
From preview (Slider.kt:282-303):
var value by remember { mutableFloatStateOf(50f) }
var isEditing by remember { mutableStateOf(false) }
Column {
Text("Interactive Slider", style = AppTheme.typography.h4)
Text(
text = if (isEditing) "Editing..." else "Value: ${value.toInt()}",
style = AppTheme.typography.body1
)
Slider(
value = value,
onValueChange = {
value = it
isEditing = true
},
valueRange = 0f..100f,
onValueChangeFinished = { isEditing = false },
modifier = Modifier.fillMaxWidth()
)
}
Custom Colors Slider
From preview (Slider.kt:263-280):
var value by remember { mutableFloatStateOf(0.5f) }
Column {
Text("Custom Colors", style = AppTheme.typography.h4)
Slider(
value = value,
onValueChange = { value = it },
colors = SliderDefaults.colors(
thumbColor = AppTheme.colors.error,
activeTrackColor = AppTheme.colors.error,
inactiveTrackColor = AppTheme.colors.error.copy(alpha = 0.3f)
),
modifier = Modifier.fillMaxWidth()
)
}
Basic RangeSlider
From preview (Slider.kt:326-337):
var range by remember { mutableStateOf(0.2f..0.8f) }
Column {
Text("Basic Range Slider", style = AppTheme.typography.h4)
RangeSlider(
value = range,
onValueChange = { range = it },
modifier = Modifier.fillMaxWidth()
)
}
RangeSlider with Values Display
From preview (Slider.kt:353-379):
var range by remember { mutableStateOf(20f..80f) }
Column {
Text("Custom Range (0-100)", style = AppTheme.typography.h4)
Column {
RangeSlider(
value = range,
onValueChange = { range = it },
valueRange = 0f..100f,
modifier = Modifier.fillMaxWidth()
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = "Start: ${range.start.toInt()}",
style = AppTheme.typography.body1
)
Text(
text = "End: ${range.endInclusive.toInt()}",
style = AppTheme.typography.body1
)
}
}
}
Stepped RangeSlider
From preview (Slider.kt:339-351):
var range by remember { mutableStateOf(0.2f..0.6f) }
Column {
Text("Stepped Range Slider (5 steps)", style = AppTheme.typography.h4)
RangeSlider(
value = range,
onValueChange = { range = it },
steps = 4,
modifier = Modifier.fillMaxWidth()
)
}
SliderColors
The SliderColors class provides color customization (Slider.kt:147-171):
@Composable
fun SliderDefaults.colors(
thumbColor: Color = AppTheme.colors.primary,
activeTrackColor: Color = AppTheme.colors.primary,
activeTickColor: Color = AppTheme.colors.onPrimary,
inactiveTrackColor: Color = AppTheme.colors.secondary,
inactiveTickColor: Color = AppTheme.colors.primary,
disabledThumbColor: Color = AppTheme.colors.disabled,
disabledActiveTrackColor: Color = AppTheme.colors.disabled,
disabledActiveTickColor: Color = AppTheme.colors.disabled,
disabledInactiveTrackColor: Color = AppTheme.colors.disabled,
disabledInactiveTickColor: Color = Color.Unspecified,
): SliderColors
Advanced Usage
Volume Control
var volume by remember { mutableFloatStateOf(50f) }
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
Icon(
imageVector = if (volume > 0) Icons.Default.VolumeUp else Icons.Default.VolumeOff,
contentDescription = "Volume"
)
Slider(
value = volume,
onValueChange = { volume = it },
valueRange = 0f..100f,
modifier = Modifier.weight(1f)
)
Text("${volume.toInt()}%")
}
Price Range Filter
var priceRange by remember { mutableStateOf(100f..500f) }
val minPrice = 0f
val maxPrice = 1000f
Column {
Text("Price Range", style = AppTheme.typography.h4)
RangeSlider(
value = priceRange,
onValueChange = { priceRange = it },
valueRange = minPrice..maxPrice,
steps = 9, // 10 positions: 0, 100, 200, ..., 1000
modifier = Modifier.fillMaxWidth()
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text("$${priceRange.start.toInt()}")
Text("$${priceRange.endInclusive.toInt()}")
}
}
Brightness Control with Icon
var brightness by remember { mutableFloatStateOf(0.7f) }
Column {
Text("Brightness", style = AppTheme.typography.h4)
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
Icon(
imageVector = Icons.Default.BrightnessLow,
contentDescription = null,
tint = AppTheme.colors.textSecondary
)
Slider(
value = brightness,
onValueChange = { brightness = it },
modifier = Modifier.weight(1f)
)
Icon(
imageVector = Icons.Default.BrightnessHigh,
contentDescription = null,
tint = AppTheme.colors.primary
)
}
}
Styling
Track and Thumb
The slider uses the BasicSlider and BasicRangeSlider components from the com.nomanr.composables.slider package, which handle the visual rendering.
Steps Visualization
When steps > 0, tick marks are displayed on the track:
- Active ticks: On the filled portion (use
activeTickColor)
- Inactive ticks: On the unfilled portion (use
inactiveTickColor)
Accessibility
- Sliders are keyboard accessible
- Support for screen readers through semantic properties
- Clear visual distinction between active and inactive track
- Disabled state indicated with reduced opacity colors
- Adequate thumb size for touch interaction
Best Practices
When to Use Steps
- Use steps when you need discrete values (e.g., rating 1-5)
- For continuous values like volume or brightness, use
steps = 0
- The number of steps should match your data model
Value Range
- Always set
valueRange to match your expected value range
- This makes the component more intuitive and avoids conversion logic
Callbacks
- Use
onValueChange for real-time updates during dragging
- Use
onValueChangeFinished for actions after the user releases (e.g., save to database, apply filter)
Source Reference
Full implementation: com.nomanr.lumo.ui.components.Slider.kt