Compose Component

ScrollField

ScrollField's can be used to provide a more interactive way to select a time or other numerical value.

ScrollField social preview

ScrollFieldSample

@Sampled
@Composable
@Preview
fun ScrollFieldSample() {
    val minVal = 1000
    val maxVal = 2000
    val itemCount = (maxVal - minVal) + 1

    val state = rememberScrollFieldState(itemCount = itemCount, index = 0)
    var selectedValue by remember { mutableIntStateOf(minVal) }
    val colors = ScrollFieldDefaults.colors()

    Row(
        modifier =
            Modifier.background(
                    MaterialTheme.colorScheme.surfaceContainerHighest,
                    RoundedCornerShape(28.dp),
                )
                .padding(12.dp),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.spacedBy(8.dp),
    ) {
        ScrollField(
            state = state,
            modifier = Modifier.size(width = 192.dp, height = 160.dp),
            contentDescription = "Select value between 1000 and 2000",
            fieldAccessibilityDescription = { index -> (minVal + index).toString() },
            field = { index, selected, enabled ->
                val valueToShow = minVal + index
                Box(modifier = Modifier.fillMaxHeight(), contentAlignment = Alignment.Center) {
                    Text(
                        text = valueToShow.toString(),
                        style =
                            if (selected) {
                                MaterialTheme.typography.displayLargeEmphasized
                            } else {
                                MaterialTheme.typography.displayMedium
                            },
                        color =
                            when {
                                enabled && selected -> colors.selectedContentColor
                                enabled && !selected -> colors.contentColor
                                !enabled && selected -> colors.disabledSelectedContentColor
                                else -> colors.disabledContentColor
                            },
                    )
                }
            },
        )
    }
}

TimeScrollFieldSample

@Sampled
@Composable
@Preview
fun TimeScrollFieldSample() {
    val hourCount = 24
    val minuteCount = 60

    val hourState = rememberScrollFieldState(itemCount = hourCount, index = 12)
    val minuteState = rememberScrollFieldState(itemCount = minuteCount, index = 30)

    var selectedHour by remember { mutableIntStateOf(12) }
    var selectedMinute by remember { mutableIntStateOf(30) }

    Row(
        modifier =
            Modifier.background(
                    MaterialTheme.colorScheme.surfaceContainerHighest,
                    RoundedCornerShape(28.dp),
                )
                .padding(12.dp),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.spacedBy(8.dp),
    ) {
        ScrollField(
            state = hourState,
            modifier = Modifier.size(width = 100.dp, height = 120.dp),
            contentDescription = "Select hour",
            fieldAccessibilityDescription = { index ->
                if (index == 1) "$index hour" else "$index hours"
            },
        )

        Text(
            text = ":",
            style = MaterialTheme.typography.displayLarge,
            color = MaterialTheme.colorScheme.onSurfaceVariant,
            textAlign = TextAlign.Center,
            modifier = Modifier.offset(y = (-4).dp).semantics { hideFromAccessibility() },
        )

        ScrollField(
            state = minuteState,
            modifier = Modifier.size(width = 100.dp, height = 120.dp),
            contentDescription = "Select minute",
            fieldAccessibilityDescription = { index ->
                if (index == 1) "$index minute" else "$index minutes"
            },
        )
    }
}

UnitScrollFieldSample

@Sampled
@Composable
@Preview
fun UnitScrollFieldSample() {
    val amountCount = 100 // 0.1 to 10.0
    val unitCount = 2 // L, oz

    val amountState = rememberScrollFieldState(itemCount = amountCount, index = 26) // 2.7
    val unitState = rememberScrollFieldState(itemCount = unitCount, index = 0) // L

    val units = listOf("L", "oz")

    val colors = ScrollFieldDefaults.colors()

    Row(
        modifier =
            Modifier.background(
                    MaterialTheme.colorScheme.surfaceContainerHighest,
                    RoundedCornerShape(28.dp),
                )
                .padding(12.dp),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.spacedBy(8.dp),
    ) {
        ScrollField(
            state = amountState,
            modifier = Modifier.size(width = 120.dp, height = 160.dp),
            contentDescription = "Select amount",
            fieldAccessibilityDescription = { index -> ((index + 1) / 10.0).toString() },
            field = { index, selected, enabled ->
                val amount = (index + 1) / 10.0
                Box(modifier = Modifier.fillMaxHeight(), contentAlignment = Alignment.Center) {
                    Text(
                        text = "${(amount * 10).toInt() / 10.0}",
                        style =
                            if (selected) {
                                MaterialTheme.typography.displayLargeEmphasized
                            } else {
                                MaterialTheme.typography.displayMedium
                            },
                        color =
                            when {
                                enabled && selected -> colors.selectedContentColor
                                enabled && !selected -> colors.contentColor
                                !enabled && selected -> colors.disabledSelectedContentColor
                                else -> colors.disabledContentColor
                            },
                    )
                }
            },
        )

        ScrollField(
            state = unitState,
            modifier = Modifier.size(width = 120.dp, height = 160.dp),
            contentDescription = "Select unit",
            fieldAccessibilityDescription = { index -> units[index] },
            field = { index, selected, enabled ->
                Box(modifier = Modifier.fillMaxHeight(), contentAlignment = Alignment.Center) {
                    Text(
                        text = units[index],
                        style =
                            if (selected) {
                                MaterialTheme.typography.displayLargeEmphasized
                            } else {
                                MaterialTheme.typography.displayMedium
                            },
                        color =
                            when {
                                enabled && selected -> colors.selectedContentColor
                                enabled && !selected -> colors.contentColor
                                !enabled && selected -> colors.disabledSelectedContentColor
                                else -> colors.disabledContentColor
                            },
                    )
                }
            },
        )
    }
}
Jetpack Compose beyond MaterialFree components, screens and templates for apps that don’t look like Google’s. Copy them into your app, or let your coding agent add them.Browse for free

Content updated: