compose-datetime-wheel-picker
1.3.2indexedElliptical datetime picker implementation featuring customizable wheel pickers for date, time, and datetime selection, supporting various formats and theming options.
Elliptical datetime picker implementation featuring customizable wheel pickers for date, time, and datetime selection, supporting various formats and theming options.
English | 简体中文
Highly customizable wheel pickers for date, time and date-time selection, built with Compose Multiplatform. Rows sit on a cylindrical barrel like a native iOS picker, and 30 languages work out of the box.
[!NOTE] Upgrading from 1.4.x?
rowCountis replaced byrows. See MIGRATION.md and CHANGELOG.md.
| Picker | Basic usage |
|---|
Try it in your browser: the sample app compiled to Wasm, with every picker and the barrel variants.
Add the dependencies to your version catalog (gradle/libs.versions.toml):
[versions]
datetime-wheel-picker = "1.5.0"
kotlinx-datetime = "0.8.0"
[libraries]
datetime-wheel-picker = { module = "io.github.darkokoa:datetime-wheel-picker", version.ref = "datetime-wheel-picker" }
kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinx-datetime" }
In a Compose Multiplatform project, add them to commonMain:
kotlin {
sourceSets {
commonMain.dependencies {
implementation(libs.datetime.wheel.picker)
implementation(libs.kotlinx.datetime) // the pickers expose its LocalDate, LocalTime, ...
}
}
}
In a single-platform project (for example Android), use the dependencies block instead:
dependencies {
implementation(libs.datetime.wheel.picker)
implementation(libs.kotlinx.datetime)
}
Artifacts are published to Maven Central, so make sure mavenCentral() is in your repositories.
implementation("io.github.darkokoa:datetime-wheel-picker:<version>")
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.8.0")
Enable core library desugaring:
android {
compileOptions {
isCoreLibraryDesugaringEnabled = true
}
}
dependencies {
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5")
}
| Library | Kotlin |
|---|
These are the versions each release is built with. Targets: android, jvm, iosArm64,
iosSimulatorArm64, js and wasmJs.
import dev.darkokoa.datetimewheelpicker.WheelDatePicker
import dev.darkokoa.datetimewheelpicker.WheelDateTimePicker
import dev.darkokoa.datetimewheelpicker.WheelTimePicker
@Composable
fun Pickers() {
// Final value, delivered once the wheel settles
WheelDatePicker { snappedDate -> /* LocalDate */ }
WheelTimePicker { snappedTime -> /* LocalTime */ }
WheelDateTimePicker { snappedDateTime -> }
WheelDatePicker(
onSnappedDateChanged = { date -> },
onSnappedDate = { date -> },
)
}
Limit the range, start somewhere specific and size the picker with a Modifier:
WheelDatePicker(
modifier = Modifier.fillMaxWidth().height(200.dp),
startDate = LocalDate(2026, 10, 20),
minDate = LocalDate(2026, 1, 1),
maxDate = LocalDate(2026, 12, 31),
) { snappedDate -> }
Rows are projected onto a vertical cylinder. rows chooses whether the row count or the row
height stays constant, and barrelProperties sets how strongly the drum bends and fades:
// Three rows with a gentle curve (the default)
WheelDatePicker { }
// Five rows from rim to rim
WheelDatePicker(rows = WheelRows.Count(5)) { }
// iOS style: 32.dp rows, as many as fit, on a full half cylinder
WheelDateTimePicker(
modifier = Modifier.size(280.dp, 240.dp),
rows = WheelRows.Height(32.dp),
barrelProperties = WheelPickerDefaults.barrelProperties(rimAngle = 90f),
) { }
// Flat list, every row opaque
WheelDatePicker(
barrelProperties = WheelPickerDefaults.barrelProperties(rimAngle = 0f, fadeStrength = 0f),
) { }
Details, defaults and the maths are in Rows and barrel projection.
WheelDateTimePicker(
dateFormatter = dateFormatter(
locale = Locale.current,
monthDisplayStyle = MonthDisplayStyle.SHORT,
cjkSuffixConfig = CjkSuffixConfig.HideAll,
),
timeFormatter = timeFormatter(timeFormat = TimeFormat.HOUR_24),
textStyle = MaterialTheme.typography.titleSmall,
textColor = Color(0xFFffc300),
selectedTextStyle = MaterialTheme.typography.titleSmall.copy(fontWeight = FontWeight.Bold),
selectedTextColor = Color.Black,
selectorProperties = WheelPickerDefaults.selectorProperties(
shape = RoundedCornerShape(0.dp),
color = Color(0xFFf1faee).copy(alpha = 0.2f),
border = BorderStroke(2.dp, Color(0xFFf1faee)),
),
) { snappedDateTime -> }
More configurations, such as a day-month picker without a year, a birth-date range, or Chinese/Japanese/Korean suffixes, are in Recipes.
The pickers follow Locale.current: month names, AM/PM text, date order (for example MDY for
en-US, YMD for CJK), 12/24-hour time and numerals. Supported languages include Arabic, Chinese,
English, French, German, Hindi, Japanese, Korean, Portuguese, Russian, Spanish and more, 30 in
total. See Localization for the full list and how locales are matched.
Spotted a translation error or want another language? Please open an issue or send a pull request.
The docs/ pages are written in English.
The sample/ module is a Compose Multiplatform app with demos for every picker, including the
barrel variants. A Wasm build is hosted at https://darkokoa.github.io/compose-datetime-wheel-picker/ and
redeployed on every push to main (see pages.yml).
To run it yourself, open the project in Android Studio or IntelliJ IDEA and run sample:androidApp,
the iOS app in sample/iosApp, or the Desktop entry point (main.kt in
sample/composeApp/src/jvmMain). To run the web version locally:
./gradlew :sample:composeApp:wasmJsBrowserDevelopmentRun
Issues and pull requests are welcome. Please run ./gradlew check before sending a pull request.
Released under the Apache License, Version 2.0.
Inspired by WheelPickerCompose. The barrel projection builds on a contribution by @bnrdk (#150).
| Date and time | WheelDateTimePicker { snappedDateTime -> } |
| Date | WheelDatePicker { snappedDate -> } |
| Time (24-hour) | WheelTimePicker { snappedTime -> } |
| Time (AM/PM) | WheelTimePicker(timeFormatter = timeFormatter(timeFormat = TimeFormat.AM_PM)) { snappedTime -> } |
WheelTextPicker for your own values.WheelRows.Count(n) or WheelRows.Height(h).fillMaxWidth(), size(), weight() and friends just work.| Compose Multiplatform |
|---|
| kotlinx-datetime |
|---|
| Android minSdk |
|---|
| 1.5.0 | 2.4.20 | 1.12.1 | 0.8.0 | 21 |
| 1.4.0 | 2.4.0 | 1.11.1 | 0.8.0 | 21 |
| API reference | Parameters, defaults, callbacks and formatters |
| Rows and barrel projection | WheelRows, rim angle and fade |
| Sizing | How Modifier constraints resolve to a picker size |
| Recipes | Ready-to-use configurations |
| Localization | Languages, locale matching, numerals |
| Migration guide | Upgrading between releases |
| Changelog | What changed in each release |
Surfaced from shared tags and platforms — no rankings paid for.