Picker

Functions summary

Unit
@Composable
Picker(
    state: PickerState,
    modifier: Modifier,
    readOnly: Boolean,
    readOnlyLabel: (@Composable BoxScope.() -> Unit)?,
    scalingParams: ScalingParams,
    separation: Dp,
    gradientRatio: @FloatRange(from = 0.0, to = 0.5) Float,
    gradientColor: Color,
    flingBehavior: FlingBehavior,
    option: @Composable PickerScope.(optionIndex: Int) -> Unit
)

This function is 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].

Unit
@Composable
Picker(
    state: PickerState,
    contentDescription: String?,
    modifier: Modifier,
    readOnly: Boolean,
    readOnlyLabel: (@Composable BoxScope.() -> Unit)?,
    onSelected: () -> Unit,
    scalingParams: ScalingParams,
    separation: Dp,
    gradientRatio: @FloatRange(from = 0.0, to = 0.5) Float,
    gradientColor: Color,
    flingBehavior: FlingBehavior,
    userScrollEnabled: Boolean,
    rotaryScrollableBehavior: RotaryScrollableBehavior?,
    option: @Composable PickerScope.(optionIndex: Int) -> Unit
)

A scrollable list of items to pick from.

Functions

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

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.

Example of a simple picker to select one of five options:

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.wear.compose.material.Picker
import androidx.wear.compose.material.Text
import androidx.wear.compose.material.rememberPickerState

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])
    }
}

Example of dual pickers, where clicking switches which one is editable and which is read-only:

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInteropFilter
import androidx.compose.ui.unit.dp
import androidx.wear.compose.material.MaterialTheme
import androidx.wear.compose.material.Picker
import androidx.wear.compose.material.Text
import androidx.wear.compose.material.rememberPickerState

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)) },
    )
}
Parameters
state: PickerState

The state of the component

modifier: Modifier = Modifier

Modifier to be applied to the Picker

readOnly: Boolean = false

Determines 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.

readOnlyLabel: (@Composable BoxScope.() -> Unit)? = null

A 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.

scalingParams: ScalingParams = PickerDefaults.scalingParams()

The parameters to configure the scaling and transparency effects for the component. See ScalingParams

separation: Dp = 0.dp

The amount of separation in Dp between items. Can be negative, which can be useful for Text if it has plenty of whitespace.

gradientRatio: @FloatRange(from = 0.0, to = 0.5) Float = PickerDefaults.DefaultGradientRatio

The 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.

gradientColor: Color = MaterialTheme.colors.background

Should be the color outside of the Picker, so there is continuity.

flingBehavior: FlingBehavior = PickerDefaults.flingBehavior(state)

logic describing fling behavior.

option: @Composable PickerScope.(optionIndex: Int) -> Unit

A 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
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,
    gradientRatio: @FloatRange(from = 0.0, to = 0.5) 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
): Unit

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 supports rotary input. Rotary input allows users to scroll the content of the Picker - by using a crown or a rotating bezel on their Wear OS device. It can be modified with rotaryScrollableBehavior param. Note that rotary scroll and touch scroll should be aligned. If rotaryScrollableBehavior is set for snap (using RotaryScrollableDefaults.snapBehavior), flingBehavior should be set for snap as well (using PickerDefaults.flingBehavior).

Example of a simple picker to select one of five options:

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.wear.compose.material.Picker
import androidx.wear.compose.material.Text
import androidx.wear.compose.material.rememberPickerState

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])
    }
}

Example of dual pickers, where clicking switches which one is editable and which is read-only:

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInteropFilter
import androidx.compose.ui.unit.dp
import androidx.wear.compose.material.MaterialTheme
import androidx.wear.compose.material.Picker
import androidx.wear.compose.material.Text
import androidx.wear.compose.material.rememberPickerState

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)) },
    )
}
Parameters
state: PickerState

The state of the component

contentDescription: String?

Text 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 */ } }

modifier: Modifier = Modifier

Modifier to be applied to the Picker

readOnly: Boolean = false

Determines 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.

readOnlyLabel: (@Composable BoxScope.() -> Unit)? = null

A 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.

onSelected: () -> Unit = {}

Action triggered when the Picker is selected by clicking. Used by accessibility semantics, which facilitates implementation of multi-picker screens.

scalingParams: ScalingParams = PickerDefaults.defaultScalingParams()

The parameters to configure the scaling and transparency effects for the component. See ScalingParams

separation: Dp = 0.dp

The amount of separation in Dp between items. Can be negative, which can be useful for Text if it has plenty of whitespace.

gradientRatio: @FloatRange(from = 0.0, to = 0.5) Float = PickerDefaults.DefaultGradientRatio

The 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.

gradientColor: Color = MaterialTheme.colors.background

Should be the color outside of the Picker, so there is continuity.

flingBehavior: FlingBehavior = PickerDefaults.flingBehavior(state)

logic describing fling behavior. Note that when configuring fling or snap behavior, this flingBehavior parameter and the rotaryScrollableBehavior parameter that controls rotary scroll are expected to be consistent.

userScrollEnabled: Boolean = true

Determines whether the picker should be scrollable or not. When userScrollEnabled = true, picker is scrollable. This is different from readOnly as it changes the scrolling behaviour.

rotaryScrollableBehavior: RotaryScrollableBehavior? = RotaryScrollableDefaults.snapBehavior(state, state.toRotarySnapLayoutInfoProvider())

Parameter for changing rotary behavior. Supports scroll RotaryScrollableDefaults.behavior and snap RotaryScrollableDefaults.snapBehavior. We do recommend to use RotaryScrollableDefaults.snapBehavior as this is a recommended behavior for Pickers. Note that when configuring fling or snap behavior, this rotaryBehavior parameter and the flingBehavior parameter that controls touch scroll are expected to be consistent. Can be null if rotary support is not required.

option: @Composable PickerScope.(optionIndex: Int) -> Unit

A 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.