ScrollField's can be used to provide a more interactive way to select a time or other numerical value.
@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) }
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, isSelected ->
val valueToShow = minVal + index
Box(modifier = Modifier.fillMaxHeight(), contentAlignment = Alignment.Center) {
Text(
text = valueToShow.toString(),
style =
if (isSelected) {
MaterialTheme.typography.displayLargeEmphasized
} else {
MaterialTheme.typography.displayMedium
},
color =
if (isSelected) {
MaterialTheme.colorScheme.onSurface
} else {
MaterialTheme.colorScheme.outline
},
)
}
},
)
}
}
@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"
},
)
}
}
@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")
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, isSelected ->
val amount = (index + 1) / 10.0
Box(modifier = Modifier.fillMaxHeight(), contentAlignment = Alignment.Center) {
Text(
text = "${(amount * 10).toInt() / 10.0}",
style =
if (isSelected) {
MaterialTheme.typography.displayLargeEmphasized
} else {
MaterialTheme.typography.displayMedium
},
color =
if (isSelected) {
MaterialTheme.colorScheme.onSurface
} else {
MaterialTheme.colorScheme.outline
},
)
}
},
)
ScrollField(
state = unitState,
modifier = Modifier.size(width = 120.dp, height = 160.dp),
contentDescription = "Select unit",
fieldAccessibilityDescription = { index -> units[index] },
field = { index, isSelected ->
Box(modifier = Modifier.fillMaxHeight(), contentAlignment = Alignment.Center) {
Text(
text = units[index],
style =
if (isSelected) {
MaterialTheme.typography.displayLargeEmphasized
} else {
MaterialTheme.typography.displayMedium
},
color =
if (isSelected) {
MaterialTheme.colorScheme.onSurface
} else {
MaterialTheme.colorScheme.outline
},
)
}
},
)
}
}