Composable Component

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.

Android
@Composable
public fun Picker(
    state: PickerState,
    contentDescription: (() -> String)?,
    modifier: Modifier = Modifier,
    readOnly: Boolean = false,
    readOnlyLabel: @Composable (BoxScope.() -> Unit)? = null,
    onSelected: () -> Unit = {},
    verticalSpacing: Dp = 0.dp,
    @FloatRange(from = 0.0, to = 0.5) gradientRatio: Float = PickerDefaults.GradientRatio,
    gradientColor: Color = MaterialTheme.colorScheme.background,
    userScrollEnabled: Boolean = true,
    rotaryScrollableBehavior: RotaryScrollableBehavior? = PickerDefaults.rotarySnapBehavior(state),
    option: @Composable PickerScope.(index: Int) -> Unit,
)

Parameters

state The state of the component
contentDescription A block which computes 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.
modifier Modifier to be applied to the Picker.
readOnly 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 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 Action triggered when the Picker is selected by clicking. Used by accessibility semantics, which facilitates implementation of multi-picker screens.
verticalSpacing The amount of vertical spacing in Dp between items. Can be negative, which can be useful for Text if it has plenty of whitespace.
gradientRatio 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 Should be the color outside of the Picker, so there is continuity. If using custom background (gradients, images), gradientColor must be set to Color.Unspecified
userScrollEnabled 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 Parameter for changing rotary behavior. We recommend to use PickerDefaults.rotarySnapBehavior for Pickers, but passing null turns off the rotary handling if it is not required.
option A block which describes the content. Inside this block you can reference PickerScope.selectedOptionIndex to determine which option is selected. 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.

Code Examples

PickerGroupSample

@Composable
fun PickerGroupSample() {
    var selectedPickerIndex by remember { mutableIntStateOf(0) }
    val pickerStateHour = rememberPickerState(initialNumberOfOptions = 24)
    val pickerStateMinute = rememberPickerState(initialNumberOfOptions = 60)
    Column(
        modifier = Modifier.fillMaxWidth(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        Spacer(modifier = Modifier.size(30.dp))
        val label = if (selectedPickerIndex == 0) "Hours" else "Minutes"
        AnimatedContent(targetState = label) { targetText -> Text(text = targetText) }
        Spacer(modifier = Modifier.size(10.dp))
        PickerGroup(
            selectedPickerState =
                if (selectedPickerIndex == 0) pickerStateHour else pickerStateMinute,
            autoCenter = false,
        ) {
            PickerGroupItem(
                pickerState = pickerStateHour,
                selected = selectedPickerIndex == 0,
                onSelected = { selectedPickerIndex = 0 },
                option = { optionIndex, _ -> Text(text = "%02d".format(optionIndex)) },
                contentDescription = { "Hours" },
                modifier = Modifier.size(80.dp, 100.dp),
            )
            PickerGroupItem(
                pickerState = pickerStateMinute,
                selected = selectedPickerIndex == 1,
                onSelected = { selectedPickerIndex = 1 },
                option = { optionIndex, _ -> Text(text = "%02d".format(optionIndex)) },
                contentDescription = { "Minutes" },
                modifier = Modifier.size(80.dp, 100.dp),
            )
        }
    }
}

SimplePicker

@Composable
fun SimplePicker() {
    val items = listOf("One", "Two", "Three", "Four", "Five")
    val state = rememberPickerState(items.size)
    // We forward scroll gestures from the whole screen to the Picker which makes this sample
    // accessible for 2-finger vertical scrolling.
    Box(
        modifier =
            Modifier.fillMaxSize()
                .scrollable(
                    state = state,
                    orientation = Orientation.Vertical,
                    reverseDirection = true,
                ),
        contentAlignment = Alignment.Center,
    ) {
        Text(
            modifier = Modifier.align(Alignment.TopCenter).padding(top = 10.dp),
            text = "Selected: ${items[state.selectedOptionIndex]}",
        )
        Picker(
            modifier = Modifier.size(100.dp, 100.dp),
            state = state,
            contentDescription = { "${state.selectedOptionIndex + 1}" },
        ) {
            Text(items[it])
        }
    }
}