Skip to content

State-based API

The stateless Dial overloads (degree + onDegreeChange) are great for simple cases. When you need to read state properties from outside the composable, or drive the dial programmatically, use rememberDialState instead.

rememberDialState only holds the dial’s position. All configuration lives on the Dial composable, which applies it onto the state in place — so changing config never recreates the state or resets the current degree.

val state = rememberDialState(initialDegree = 0f)

Pass the state to either Dial overload, alongside the configuration:

// With default colors
Dial(
state = state,
startDegrees = 180f,
sweepDegrees = 275f,
interval = 15f,
valueRange = 0f..100f,
colors = DialColors.default(),
)
// With custom composables
Dial(
state = state,
startDegrees = 180f,
sweepDegrees = 275f,
thumb = { s -> CustomThumb(s) },
track = { s -> CustomTrack(s) },
)

Dragging the dial writes state.degree directly, so you can read state.value / state.mappedValue back anywhere. If you also want a callback, pass onDegreeChange.

state.animateTo() is a suspend function that smoothly moves the thumb to any target degree:

val state = rememberDialState()
val scope = rememberCoroutineScope()
Dial(state = state, sweepDegrees = 360f)
Button(onClick = { scope.launch { state.animateTo(0f) } }) {
Text("Reset")
}

You can pass a custom animationSpec:

state.animateTo(
targetDegree = 180f,
animationSpec = tween(durationMillis = 600, easing = EaseInOutCubic),
)

Because state is held in your composable scope, you can read any of its properties anywhere — not just inside thumb or track:

val state = rememberDialState()
Dial(state = state, sweepDegrees = 360f, valueRange = 0f..100f)
Text("Current value: ${state.mappedValue.toInt()}")
ParameterDefaultDescription
initialDegree0fStarting rotation angle (clamped to the sweep once Dial applies its config)

Everything else is configured on the Dial composable, not on the state:

ParameterDefaultDescription
sweepDegrees360fTotal rotatable range
startDegrees0fScreen angle where degree = 0
interval0fDegrees between snap points (0 = continuous)
steps0Number of snap stops (overrides interval when > 0)
layoutDialLayout()How radius/center are derived from constraints
valueRange0f..1fRange that mappedValue maps to
clockwisetrueRotation direction
enabledtrueWhether drag input is accepted
onDegreeChangenullCalled with the new degree as the dial is dragged
onDegreeChangeFinishednullCalled when the user releases the dial

See DialState reference for a full description of all state properties.