Skip to content

List Picker

Applications often present a long list of items and allow users to express their choice in either single or multiple selections.

List Picker is designed to handle such a complex list with ease and allows developers to:

  • Set custom views to present the target items.
  • Bind items by their position.
  • Track user interactions and selections and notify the application of the changes using callbacks.

The List Picker is displayed in a Bottom Sheet when viewed on a phone and in an Alert Dialog when viewed on a tablet.

List Picker on phone:

List Picker Phone

List Picker on tablet:

List Picker Tablet

It can be configured to allow:

  • Single selection: Presents the list of items with radio buttons and allows only one selection.

List Picker Single Selection

  • Multi selection: Presents the list of items with check boxes and allows users to make multiple selections.

List Picker Multi Selection

API

The ListPicker Composable is the main entry point for displaying the list. Its API is as follows:

@Composable
fun ListPicker(
    items: @RawValue List<ListPickerItem>,
    data: ListPickerData,
    modifier: Modifier = Modifier,
    onSelectionUpdated: ((Set<Int>) -> Unit)? = null,
    colors: ListPickerColors = ListPickerDefaults.colors(),
    textStyles: ListPickerTextStyles = ListPickerDefaults.textStyles()
)

The three main parameters of the ListPicker Composable are the ListPickerItem items, the ListPickerData data and the onSelectionUpdated Lambda, of which the first two are required parameters. They are described in more detail below:

The ListPickerItem Data Class is used to define the items in the list. It accepts any Composable which is then displayed as the list item.

data class ListPickerItem(
    val item: @RawValue @Composable () -> Unit,
    val contentDescription: CharSequence
)

The ListPickerData Data Class defines the behavior of the List Picker. It is used to set the List Picker properties, such as label, whether it is single-select or multi-select, the picker's title and more.

data class ListPickerData(
    val enabled: Boolean = true,
    val label: CharSequence? = null,
    val displayValue: CharSequence? = null,
    val hint: CharSequence? = null,
    val pickerTitle: CharSequence? = null,
    val isSingleSelect: Boolean = false,
    val showExpanded: Boolean = false,
    val showSelectorOnStart: Boolean = true,
    val autoSaveSelection: Boolean = false,
    val anchorButtonText: CharSequence? = null,
    val selectedItems: MutableSet<Int> = mutableSetOf(),
    val selectedSectionData: @RawValue SelectedSectionData = SelectedSectionData(),
    val exitDialogData: @RawValue ExitDialogData = ExitDialogData(),
    val footerData: @RawValue FooterData = FooterData(),
    val validationData: @RawValue ValidationData = ValidationData()
)

The onSelectionUpdated parameter is a Lambda expression that returns a Set of the indices of the items selected by the user.

val onSelectionUpdatedLambda: (Set<Int>) -> Unit = { selectedList ->
    // Do something with the selectedList e.g. update the display value based on the selections
    updateDisplayValue(selectedList)
}

Usage in an Application

A basic example of how to use the List Picker in an application is as follows:

// Function to create a list of Text items
fun stringListPickerItemList(count: Int): List<ListPickerItem> {
    val textList = mutableListOf<ListPickerItem>()

    for (i in 0 until count) {
        val textItem = "Item $i"
        textList.add(ListPickerItem(item = {
            Text(text = textItem,
                modifier = Modifier.clearAndSetSemantics { })
        }, contentDescription = textItem))
    }

    return textList
}

val listOfString = stringListPickerItemList(100)

// Value to be displayed for the selected items
var displayValue by rememberSaveable {
    mutableStateOf("")
}

// Lambda to update the display value when user selection changes
val onSelectionUpdated: (Set<Int>) -> Unit = { selectedList ->
    displayValue = formatDisplayValue(listOfString, selectedList)
}

// Function to format the display value
fun formatDisplayValue(dataList: List<Any>, selectedList: Set<Int>): String {
    var displayValue = ""

    if (selectedList.isEmpty()) {
        displayValue = ""
     } else {
        for ((index, value) in selectedList.withIndex()) {
            var item = dataList[value]

            displayValue = if (index == 0) {
                item.toString()
            } else {
                "$displayValue, $item"
            }
        }
    }
    return displayValue
}

// Actual List Picker Composable
@Composable
ListPicker(
    items = listOfString,
    data = ListPickerData(
        label = "Multi Selection List Picker",
        hint = "Select",
        pickerTitle = "Select Item",
        displayValue = displayValue
    ),
    onSelectionUpdated = onSelectionUpdated
)

List Picker for String Items

The SDK also provides a convenience ListPicker Composable which can be used for String items. This API accepts a List of String instead of a List of ListPickerItem.

@Composable
fun ListPicker(
    stringItems: List<String>,
    data: ListPickerData,
    modifier: Modifier = Modifier,
    onSelectionUpdated: ((Set<Int>) -> Unit)? = null,
    colors: ListPickerColors = ListPickerDefaults.colors(),
    textStyles: ListPickerTextStyles = ListPickerDefaults.textStyles()
)

And this can then be used as follows:

// Function to create list of Strings
fun stringList(count: Int): List<String> {
    val stringList = mutableListOf<String>()

    for (i in 0 until count) {
        stringList.add("Item $i")
    }

    return stringList
}

val listOfString = stringList(100)

// Value to be displayed for the selected items
var displayValue by rememberSaveable {
    mutableStateOf("")
}

// Lambda to update the display value when user selection changes
val onSelectionUpdated: (Set<Int>) -> Unit = { selectedList ->
    displayValue = formatDisplayValue(listOfString, selectedList)
}

// Function to format the display value
fun formatDisplayValue(dataList: List<Any>, selectedList: Set<Int>): String {
    var displayValue = ""

    if (selectedList.isEmpty()) {
        displayValue = ""
     } else {
        for ((index, value) in selectedList.withIndex()) {
            var item = dataList[value]

            displayValue = if (index == 0) {
                item.toString()
            } else {
                "$displayValue, $item"
            }
        }
    }
    return displayValue
}

// Actual List Picker Composable
@Composable
ListPicker(
    stringItems = listOfString,
    data = ListPickerData(
        label = "Multi Selection List Picker",
        hint = "Select",
        pickerTitle = "Select Item",
        displayValue = displayValue
    ),
    onSelectionUpdated = onSelectionUpdated
)

List Picker Validation

List Picker supports a validation icon and message that can be used to validate user input. This can be configured using the ValidationData Data Class. Default icons are provided as well, based on whether error is enabled or not.

data class ValidationData(
    val enabled: Boolean = false,
    val isError: Boolean = false,
    val message: CharSequence? = null,
    val icon: FioriIcon? = FioriIcon(
        resId = if (isError) {
            R.drawable.ic_sap_icon_hint
        } else {
            R.drawable.ic_sap_icon_accept
        }
    )
)

To show an error icon and message:

@Composable
ListPicker(
    stringItems = stringList,
    data = ListPickerData(
        label = "Validation List Picker",
        hint = "Select",
        pickerTitle = "Choose Item",
        isSingleSelect = true,
        validationData = ValidationData(
            enabled = true,
            isError = true,
            message = "Please select at least one option"
        )
    )
)

List Picker Validation Error

To show a regular validation icon and message:

@Composable
ListPicker(
    stringItems = stringList,
    data = ListPickerData(
        label = "Validation List Picker",
        hint = "Select",
        pickerTitle = "Choose Item",
        isSingleSelect = true,
        validationData = ValidationData(
            enabled = true,
            isError = false,
            message = "Option selected"
        )
    )
)

List Picker Validation

Expanded List Picker in Phone

By default, the List Picker is displayed in a half-expanded Bottom Sheet when viewed on a phone. This behavior can be changed to display it in a fully-expanded state. This can be achieved as follows:

@Composable
ListPicker(
    stringItems = stringList,
    data = ListPickerData(
        label = "Expanded List Picker",
        hint = "Select",
        pickerTitle = "Choose Item",
        showExpanded = true
    )
)

Exit Dialog

When users make a selection and click on the Close button, an Exit Dialog is displayed, which confirms whether the user wants to save or discard their selection. This behavior is enabled by default. The dialog title, message, and button text are all customizable. Application developers can also choose to not show the Exit Dialog. All of this can be done using the ExitDialogData Data Class.

data class ExitDialogData(
    val showDialog: Boolean = true,
    val title: CharSequence? = null,
    val message: CharSequence? = null,
    val confirmButtonText: CharSequence? = null,
    val dismissButtonText: CharSequence? = null
)

@Composable
ListPicker(
    stringItems = stringList,
    data = ListPickerData(
        label = "Expanded List Picker",
        hint = "Select",
        pickerTitle = "Choose Item",
        exitDialogData = ExitDialogData(showDialog = false)
    )
)

List Picker Exit Dialog

Auto Save Single Selection List Picker

In the case of single selection, the List Picker can be configured to save the selection and close the picker without the user having to click on the Apply button. When this is enabled, the footer can also optionally be hidden so that the Apply button is not displayed. This can be achieved as follows:

@Composable
ListPicker(
    stringItems = stringList,
    data = ListPickerData(
        label = "Expanded List Picker",
        hint = "Select",
        pickerTitle = "Choose Item",
        isSingleSelect = true,
        autoSaveSelection = true,
        footerData = FooterData(enabled = false)
    )
)

Left to Right

The List Picker allows you to position the selector (checkbox or radio) button on either left(start) or right(end) of the item view. This can be done as follows:

@Composable
ListPicker(
    stringItems = stringList,
    data = ListPickerData(
        label = "Expanded List Picker",
        hint = "Select",
        pickerTitle = "Choose Item",
        showSelectorOnStart = false
    )
)

List Picker Selector End


Last update: June 15, 2023