New Compose Multiplatform components arrived on Composables UICheck it out →

Picker

A scrollable list of items to pick from. By default, items will be repeated "infinitely" in both directions, unless [PickerState#repeatItems] is specified as false.

This overload has default support for rotary side button (crown) and bezel input devices. The content will be scrolled when the rotary device is rotated with a snap motion which will snap each item to the centre of the list while it is rotated. It uses [RotaryScrollableDefaults.snapBehavior]. This behavior can be modified using the alternative Picker overload that takes a custom rotaryBehavior parameter.

Last updated:

Installation

dependencies {
   implementation("androidx.wear.compose:compose-material:1.4.0-beta03")
}

Overloads

@Composable
@Deprecated(
    "Please use the new overload with additional rotaryBehavior parameter",
    level = DeprecationLevel.HIDDEN

fun Picker(
    state: PickerState,
    contentDescription: String?,
    modifier: Modifier = Modifier,
    readOnly: Boolean = false,
    readOnlyLabel: @Composable (BoxScope.() -> Unit)? = null,
    onSelected: () -> Unit = {},
    scalingParams: ScalingParams = PickerDefaults.defaultScalingParams(),
    separation: Dp = 0.dp,
    @FloatRange(from = 0.0, to = 0.5)
    gradientRatio: Float = PickerDefaults.DefaultGradientRatio,
    gradientColor: Color = MaterialTheme.colors.background,
    flingBehavior: FlingBehavior = PickerDefaults.flingBehavior(state),
    userScrollEnabled: Boolean = true,
    option: @Composable PickerScope.(optionIndex: Int) -> Unit
)

Parameters

namedescription
stateThe state of the component
contentDescriptionText used by accessibility services to describe what the selected option represents. This text should be localized, such as by using [androidx.compose.ui.res.stringResource] or similar. Typically, the content description is inferred via derivedStateOf to avoid unnecessary recompositions, like this: val description by remember { derivedStateOf { /* expression using state.selectedOption */ } }
@Composable
@OptIn(ExperimentalWearFoundationApi::class
fun Picker(
    state: PickerState,
    contentDescription: String?,
    modifier: Modifier = Modifier,
    readOnly: Boolean = false,
    readOnlyLabel: @Composable (BoxScope.() -> Unit)? = null,
    onSelected: () -> Unit = {},
    scalingParams: ScalingParams = PickerDefaults.defaultScalingParams(),
    separation: Dp = 0.dp,
    @FloatRange(from = 0.0, to = 0.5)
    gradientRatio: Float = PickerDefaults.DefaultGradientRatio,
    gradientColor: Color = MaterialTheme.colors.background,
    flingBehavior: FlingBehavior = PickerDefaults.flingBehavior(state),
    userScrollEnabled: Boolean = true,
    rotaryScrollableBehavior: RotaryScrollableBehavior? = RotaryScrollableDefaults.snapBehavior(
        state,
        state.toRotarySnapLayoutInfoProvider()
    ),
    option: @Composable PickerScope.(optionIndex: Int) -> Unit
)

Parameters

namedescription
stateThe state of the component
contentDescriptionText used by accessibility services to describe what the selected option represents. This text should be localized, such as by using [androidx.compose.ui.res.stringResource] or similar. Typically, the content description is inferred via derivedStateOf to avoid unnecessary recompositions, like this: val description by remember { derivedStateOf { /* expression using state.selectedOption */ } }
@Composable
@Deprecated("This overload is provided for backwards compatibility with Compose for Wear OS 1.1." +
    "A newer overload is available with additional userScrollEnabled parameter which improves " +
    "accessibility of [Picker].", level = DeprecationLevel.HIDDEN
@Suppress("DEPRECATION"
fun Picker(
    state: PickerState,
    contentDescription: String?,
    modifier: Modifier = Modifier,
    readOnly: Boolean = false,
    readOnlyLabel: @Composable (BoxScope.() -> Unit)? = null,
    onSelected: () -> Unit = {},
    scalingParams: androidx.wear.compose.material.ScalingParams = PickerDefaults.scalingParams(),
    separation: Dp = 0.dp,
    @FloatRange(from = 0.0, to = 0.5)
    gradientRatio: Float = PickerDefaults.DefaultGradientRatio,
    gradientColor: Color = MaterialTheme.colors.background,
    flingBehavior: FlingBehavior = PickerDefaults.flingBehavior(state),
    option: @Composable PickerScope.(optionIndex: Int) -> Unit
)

Parameters

namedescription
stateThe state of the component
contentDescriptionText used by accessibility services to describe what the selected option represents. This text should be localized, such as by using [androidx.compose.ui.res.stringResource] or similar. Typically, the content description is inferred via derivedStateOf to avoid unnecessary recompositions, like this: val description by remember { derivedStateOf { /* expression using state.selectedOption */ } }
@Composable
@Deprecated("This overload is provided for backwards compatibility with Compose for Wear OS 1.0." +
  "A newer overload is available with additional contentDescription, onSelected and " +
  "userScrollEnabled parameters, which improves accessibility of [Picker]."
@Suppress("DEPRECATION"
fun Picker(
    state: PickerState,
    modifier: Modifier = Modifier,
    readOnly: Boolean = false,
    readOnlyLabel: @Composable (BoxScope.() -> Unit)? = null,
    scalingParams: androidx.wear.compose.material.ScalingParams = PickerDefaults.scalingParams(),
    separation: Dp = 0.dp,
    @FloatRange(from = 0.0, to = 0.5)
    gradientRatio: Float = PickerDefaults.DefaultGradientRatio,
    gradientColor: Color = MaterialTheme.colors.background,
    flingBehavior: FlingBehavior = PickerDefaults.flingBehavior(state),
    option: @Composable PickerScope.(optionIndex: Int) -> Unit
)

Parameters

namedescription
stateThe state of the component
modifierModifier to be applied to the Picker
readOnlyDetermines whether the Picker should display other available options for this field, inviting the user to scroll to change the value. When readOnly = true, only displays the currently selected option (and optionally a label). This is intended to be used for screens that display multiple Pickers, only one of which has the focus at a time.
readOnlyLabelA slot for providing a label, displayed above the selected option when the [Picker] is read-only. The label is overlaid with the currently selected option within a Box, so it is recommended that the label is given [Alignment.TopCenter].
scalingParamsThe parameters to configure the scaling and transparency effects for the component. See [ScalingParams]
separationThe amount of separation in [Dp] between items. Can be negative, which can be useful for Text if it has plenty of whitespace.
gradientRatioThe size relative to the Picker height that the top and bottom gradients take. These gradients blur the picker content on the top and bottom. The default is 0.33, so the top 1/3 and the bottom 1/3 of the picker are taken by gradients. Should be between 0.0 and 0.5. Use 0.0 to disable the gradient.
gradientColorShould be the color outside of the Picker, so there is continuity.
flingBehaviorlogic describing fling behavior.
optionA block which describes the content. Inside this block you can reference [PickerScope.selectedOption] and other properties in [PickerScope]. When read-only mode is in use on a screen, it is recommended that this content is given [Alignment.Center] in order to align with the centrally selected Picker value.
@Composable
@Deprecated(
    "This overload is provided for backwards compatibility with Compose for Wear OS 1.1." +
        "A newer overload is available which uses ScalingParams from " +
        "androidx.wear.compose.foundation.lazy package", level = DeprecationLevel.HIDDEN

@Suppress("DEPRECATION"
fun Picker(
    state: PickerState,
    contentDescription: String?,
    modifier: Modifier = Modifier,
    readOnly: Boolean = false,
    readOnlyLabel: @Composable (BoxScope.() -> Unit)? = null,
    onSelected: () -> Unit = {},
    scalingParams: androidx.wear.compose.material.ScalingParams = PickerDefaults.scalingParams(),
    separation: Dp = 0.dp,
    @FloatRange(from = 0.0, to = 0.5)
    gradientRatio: Float = PickerDefaults.DefaultGradientRatio,
    gradientColor: Color = MaterialTheme.colors.background,
    flingBehavior: FlingBehavior = PickerDefaults.flingBehavior(state),
    userScrollEnabled: Boolean = true,
    option: @Composable PickerScope.(optionIndex: Int) -> Unit
)

Code Examples

SimplePicker

@Composable
@Sampled
fun SimplePicker() {
    val items = listOf("One", "Two", "Three", "Four", "Five")
    val state = rememberPickerState(items.size)
    val contentDescription by remember { derivedStateOf { "${state.selectedOption + 1}" } }
    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center
    ) {
        Text(
            modifier = Modifier.align(Alignment.TopCenter).padding(top = 10.dp),
            text = "Selected: ${items[state.selectedOption]}"
        )
        Picker(
            modifier = Modifier.size(100.dp, 100.dp),
            state = state,
            contentDescription = contentDescription,
        ) {
            Text(items[it])
        }
    }
}

DualPicker

@Composable
@Sampled
@OptIn(ExperimentalComposeUiApi::class
fun DualPicker() {
    var selectedColumn by remember { mutableStateOf(0) }
    val textStyle = MaterialTheme.typography.display1

    @Composable
    fun Option(column: Int, text: String) = Box(modifier = Modifier.fillMaxSize()) {
        Text(
            text = text, style = textStyle,
            color = if (selectedColumn == column) MaterialTheme.colors.secondary
            else MaterialTheme.colors.onBackground,
            modifier = Modifier
                .align(Alignment.Center).wrapContentSize()
                .pointerInteropFilter {
                    if (it.action == MotionEvent.ACTION_DOWN) selectedColumn = column
                    true
                }
        )
    }

    Row(
        modifier = Modifier.fillMaxSize(),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.Center,
    ) {
        val hourState = rememberPickerState(
            initialNumberOfOptions = 12,
            initiallySelectedOption = 5
        )
        val hourContentDescription by remember {
            derivedStateOf { "${hourState.selectedOption + 1 } hours" }
        }
        Picker(
            readOnly = selectedColumn != 0,
            state = hourState,
            modifier = Modifier.size(64.dp, 100.dp),
            contentDescription = hourContentDescription,
            option = { hour: Int -> Option(0, "%2d".format(hour + 1)) }
        )
        Spacer(Modifier.width(8.dp))
        Text(text = ":", style = textStyle, color = MaterialTheme.colors.onBackground)
        Spacer(Modifier.width(8.dp))
        val minuteState =
            rememberPickerState(initialNumberOfOptions = 60, initiallySelectedOption = 0)
        val minuteContentDescription by remember {
            derivedStateOf { "${minuteState.selectedOption} minutes" }
        }
        Picker(
            readOnly = selectedColumn != 1,
            state = minuteState,
            modifier = Modifier.size(64.dp, 100.dp),
            contentDescription = minuteContentDescription,
            option = { minute: Int -> Option(1, "%02d".format(minute)) }
        )
    }
}