What is the recommended solution for creating a NumberPicker Widget in Jetpack Compose? Similar to the image below. I am able to create an NumberPicker using an AndroidView within my composable but the view does not seem to allow flings or snap to position. Btw the UI below shows three NumberPickers placed in a row. It is not supposed to represent a DatePicker
4 Answers
By coincidence I've implemented a screen like that last week. I can't share the whole code here, but basically what I did was:
- Create a layout with a
DatePicker
(res/layout/date_picker.xml).
<DatePicker xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/datePicker"
android:theme="@style/DatePickerStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:calendarViewShown="false"
android:datePickerMode="spinner" />
- Then, use it in your composable function.
@Composable
fun DatePicker(
onDateSelected: (Date) -> Unit
) {
AndroidView(
modifier = Modifier.fillMaxWidth(),
factory = { context ->
val view = LayoutInflater.from(context).inflate(R.layout.date_picker, null)
val datePicker = view.findViewById<DatePicker>(R.id.datePicker)
val calendar = Calendar.getInstance() // show today by default
datePicker.init(
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH)
) { _, year, monthOfYear, dayOfMonth ->
val date = Calendar.getInstance().apply {
set(year, monthOfYear, dayOfMonth)
}.time
onSelectedDateUpdate(date)
}
datePicker
}
)
}
- Finally, use it in a
ModalBottomSheetLayout
Editing my answer... Using a NumberPicker
working as well...
AndroidView(
modifier = Modifier.fillMaxWidth(),
factory = { context ->
NumberPicker(context).apply {
setOnValueChangedListener { numberPicker, i, i2 -> }
minValue = 0
maxValue = 50
}
}
)
Here is the result.

- 18,674
- 6
- 70
- 75
-
thank you for your answer but this question was meant for the NumberPicker widget in the android.widget package. Not the DatePicker. The UI shows three NumberPicker's placed in a row. – Charles Woodson Jul 04 '21 at 16:48
-
Can we show 5 at 5 or 10 at 10? – Vahit Keskin Nov 25 '22 at 20:37
I implemented a NumberPicker in Jetpack Compose (without using AndroidView): https://gist.github.com/nhcodes/dc68c65ee586628fda5700911e44543f
Picker.kt
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun Picker(
items: List<String>,
state: PickerState = rememberPickerState(),
modifier: Modifier = Modifier,
startIndex: Int = 0,
visibleItemsCount: Int = 3,
textModifier: Modifier = Modifier,
textStyle: TextStyle = LocalTextStyle.current,
dividerColor: Color = LocalContentColor.current,
) {
val visibleItemsMiddle = visibleItemsCount / 2
val listScrollCount = Integer.MAX_VALUE
val listScrollMiddle = listScrollCount / 2
val listStartIndex = listScrollMiddle - listScrollMiddle % items.size - visibleItemsMiddle + startIndex
fun getItem(index: Int) = items[index % items.size]
val listState = rememberLazyListState(initialFirstVisibleItemIndex = listStartIndex)
val flingBehavior = rememberSnapFlingBehavior(lazyListState = listState)
val itemHeightPixels = remember { mutableStateOf(0) }
val itemHeightDp = pixelsToDp(itemHeightPixels.value)
val fadingEdgeGradient = remember {
Brush.verticalGradient(
0f to Color.Transparent,
0.5f to Color.Black,
1f to Color.Transparent
)
}
LaunchedEffect(listState) {
snapshotFlow { listState.firstVisibleItemIndex }
.map { index -> getItem(index + visibleItemsMiddle) }
.distinctUntilChanged()
.collect { item -> state.selectedItem = item }
}
Box(modifier = modifier) {
LazyColumn(
state = listState,
flingBehavior = flingBehavior,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxWidth()
.height(itemHeightDp * visibleItemsCount)
.fadingEdge(fadingEdgeGradient)
) {
items(listScrollCount) { index ->
Text(
text = getItem(index),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = textStyle,
modifier = Modifier
.onSizeChanged { size -> itemHeightPixels.value = size.height }
.then(textModifier)
)
}
}
Divider(
color = dividerColor,
modifier = Modifier.offset(y = itemHeightDp * visibleItemsMiddle)
)
Divider(
color = dividerColor,
modifier = Modifier.offset(y = itemHeightDp * (visibleItemsMiddle + 1))
)
}
}
private fun Modifier.fadingEdge(brush: Brush) = this
.graphicsLayer(compositingStrategy = CompositingStrategy.Offscreen)
.drawWithContent {
drawContent()
drawRect(brush = brush, blendMode = BlendMode.DstIn)
}
@Composable
private fun pixelsToDp(pixels: Int) = with(LocalDensity.current) { pixels.toDp() }
PickerState.kt
@Composable
fun rememberPickerState() = remember { PickerState() }
class PickerState {
var selectedItem by mutableStateOf("")
}
PickerExample.kt
@Composable
fun PickerExample() {
Surface(modifier = Modifier.fillMaxSize()) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxSize()
) {
val values = remember { (1..99).map { it.toString() } }
val valuesPickerState = rememberPickerState()
val units = remember { listOf("seconds", "minutes", "hours") }
val unitsPickerState = rememberPickerState()
Text(text = "Example Picker", modifier = Modifier.padding(top = 16.dp))
Row(modifier = Modifier.fillMaxWidth()) {
Picker(
state = valuesPickerState,
items = values,
visibleItemsCount = 3,
modifier = Modifier.weight(0.3f),
textModifier = Modifier.padding(8.dp),
textStyle = TextStyle(fontSize = 32.sp)
)
Picker(
state = unitsPickerState,
items = units,
visibleItemsCount = 3,
modifier = Modifier.weight(0.7f),
textModifier = Modifier.padding(8.dp),
textStyle = TextStyle(fontSize = 32.sp)
)
}
Text(
text = "Interval: ${valuesPickerState.selectedItem} ${unitsPickerState.selectedItem}",
modifier = Modifier.padding(vertical = 16.dp)
)
}
}
}
Preview:

- 1,206
- 8
- 20
-
Any suggestions in modifying this , for the list not to do infinite scrolling...to scroll only the items and not repeating them? – Iulia Barbu Jul 10 '23 at 10:37
-
Yes, that should be pretty easy. `items(Integer.MAX_VALUE) { index ->` makes the list scroll infinitely, so you can replace it by `items(items) { item ->`. You have to adjust a few other things too but that's the most important part. – nhcodes Jul 11 '23 at 09:12
-
What to change also to make it an non infinite list! Please if you can help – EliodeBeirut Jul 14 '23 at 16:54
-
@nhcodes can you please tell me how can we change this implementation to be a non infinite list! – EliodeBeirut Jul 17 '23 at 09:22
-
I used this picker in a bottomsheet, I want to reset the list to first position when expanding the bottom sheet. I don't havy any success in achieving that. Seems that rememberLazyListState is blocking the recomposition. – Iulia Barbu Jul 28 '23 at 10:33
I know maybe you are not looking for something like this. But since there is no such widget in compose yet and compose is all about making your way easier to build your own component. So Apart from android.widget NumberPicker, you can make something like this one. You can change the visualization more like the NumberPicker widget and add your callback and stuff.
Have you checked this one on github? ComposeNumberPicker.Kt

- 1,560
- 7
- 19
We are using this library in our compose project for number picker widget. https://github.com/ChargeMap/Compose-NumberPicker

- 1,121
- 2
- 11
- 23
-
Hello sir I want to use this library but I dont know where do I find latest version of this library to implement this to my project so please tell me that would be very big help. Thank you. – heet kanabar Sep 05 '22 at 07:23
-
1The link I have above has information on downloading . And setting up in build gradle file too. Check it . Click on green button "Code" – SoftwareGuy Sep 06 '22 at 15:29