Skip to content

darkokoa/compose-datetime-wheel-picker

Repository files navigation

datetime-wheel-picker (work-in-progress πŸ‘·πŸ”§οΈπŸ‘·β€β™€οΈβ›)

badge-android badge-jvm badge-ios badge-js badge-wasm

Compose Multiplatform datetime picker implementation featuring highly customizable wheel pickers for date, time, and datetime selection.

Picker Basic Usage
WheelDateTimePicker { snappedDateTime -> }
WheelDatePicker { snappedDate -> }
WheelTimePicker { snappedTime -> }
WheelTimePicker(timeFormatter = timeFormatter(timeFormat = TimeFormat.AM_PM)) { snappedTime -> }

Key Features

🎯 Flexible Date Field Configuration

  • Customizable date order: DMY (Day-Month-Year), MDY (Month-Day-Year), or YMD (Year-Month-Day)
  • Hide year picker: Set yearsRange = null to create day-month only or month-day only pickers
  • Custom year range: Limit year selection to specific ranges (e.g., next 10 years, last 50 years)
  • Multiple month display styles: Full names, short names, or numeric format

🌍 Internationalization & Localization

  • Auto-adapts to locale: Date order and month names automatically match the current locale
  • CJK language support: Special handling for Chinese (εΉ΄/月/ζ—₯), Japanese (εΉ΄/月/ζ—₯), Korean (λ…„/μ›”/일) with customizable year/month/day suffixes
  • Localized numerals: Supports Eastern Arabic numerals and other numeral systems

🎨 Visual Customization

  • Configurable size, row count, text style, and colors
  • Customizable selector appearance (shape, color, border)
  • Material Design integration

⏰ Time Picker Features

  • 12-hour (AM/PM) and 24-hour formats
  • Customizable time display

Common Use Cases

Day-Month Picker (No Year)

Perfect for birthdays, anniversaries, or recurring events:

WheelDatePicker(
  startDate = LocalDate(2025, 6, 15),
  yearsRange = null,  // Hides the year picker
  dateFormatter = dateFormatter(
    dateOrder = DateOrder.DMY,
    monthDisplayStyle = MonthDisplayStyle.FULL
  )
) { snappedDate ->
  // snappedDate.month and snappedDate.dayOfMonth
}

Note: This uses the non-Composable dateFormatter() overload that accepts dateOrder parameter.

Month-Day Picker (US Format)

For US-style date input without year:

WheelDatePicker(
  yearsRange = null,
  dateFormatter = dateFormatter(
    dateOrder = DateOrder.MDY,
    monthDisplayStyle = MonthDisplayStyle.SHORT
  )
) { snappedDate -> }
Limited Year Range

Restrict year selection to a specific range:

// Only allow next 10 years
WheelDatePicker(
  yearsRange = IntRange(2025, 2035),
  dateFormatter = dateFormatter(
    dateOrder = DateOrder.YMD
  )
) { snappedDate -> }

// Only allow past 50 years (for birthdate)
val currentYear = Clock.System.now()
  .toLocalDateTime(TimeZone.currentSystemDefault()).year
WheelDatePicker(
  yearsRange = IntRange(currentYear - 50, currentYear),
  dateFormatter = dateFormatter(dateOrder = DateOrder.DMY)
) { snappedDate -> }
Chinese/Japanese/Korean Format

With native year-month-day suffixes:

  • Chinese: "2025εΉ΄1月15ζ—₯"
  • Korean: "2025λ…„1μ›”15일"
  • Japanese: "2025εΉ΄1月15ζ—₯"
WheelDatePicker(
  dateFormatter = dateFormatter(
    locale = Locale("zh"),  // "zh" for Chinese, "ja" for Japanese, "ko" for Korean
    monthDisplayStyle = MonthDisplayStyle.NUMERIC,
    cjkSuffixConfig = CjkSuffixConfig.ShowAll
  )
) { snappedDate -> }

// Without suffixes
WheelDatePicker(
  dateFormatter = dateFormatter(
    locale = Locale("zh"),
    monthDisplayStyle = MonthDisplayStyle.NUMERIC,
    cjkSuffixConfig = CjkSuffixConfig.HideAll
  )
) { snappedDate -> }

Note: This uses the Composable dateFormatter() overload. Date order (YMD for CJK) is auto-detected from locale.

Numeric Month Display

Show months as numbers instead of names:

WheelDatePicker(
  dateFormatter = dateFormatter(
    dateOrder = DateOrder.DMY,  // or MDY, YMD based on your preference
    monthDisplayStyle = MonthDisplayStyle.NUMERIC
  )
) { snappedDate -> }

Full Customization Example

WheelDateTimePicker(
  startDateTime = LocalDateTime(
    year = 2025,
    month = 10,
    day = 20,
    hour = 5,
    minute = 30
  ),
  minDateTime = Clock.System
    .now()
    .toLocalDateTime(TimeZone.currentSystemDefault()),
  maxDateTime = LocalDateTime(
    year = 2025,
    month = 10,
    day = 20,
    hour = 5,
    minute = 30
  ),
  dateFormatter = dateFormatter(
    locale = Locale.current, 
    monthDisplayStyle = MonthDisplayStyle.SHORT,
    cjkSuffixConfig = CjkSuffixConfig.HideAll
  ),
  timeFormatter = timeFormatter(
    timeFormat = TimeFormat.HOUR_24
  ),
  size = DpSize(200.dp, 100.dp),
  rowCount = 5,
  textStyle = MaterialTheme.typography.titleSmall,
  textColor = Color(0xFFffc300),
  selectorProperties = WheelPickerDefaults.selectorProperties(
    enabled = true,
    shape = RoundedCornerShape(0.dp),
    color = Color(0xFFf1faee).copy(alpha = 0.2f),
    border = BorderStroke(2.dp, Color(0xFFf1faee))
  )
) { snappedDateTime -> }

API Reference

WheelDatePicker Parameters

Parameter Type Default Description
startDate LocalDate LocalDate.now() Initial selected date
minDate LocalDate LocalDate.EPOCH Minimum selectable date
maxDate LocalDate LocalDate.CYB3R_1N1T_ZOLL Maximum selectable date
yearsRange IntRange? IntRange(minDate.year, maxDate.year) Year range to display. Set to null to hide year picker
dateFormatter DateFormatter Auto-detected Controls date order, month style, and CJK suffixes
size DpSize DpSize(256.dp, 128.dp) Picker dimensions
rowCount Int 3 Number of visible rows in the wheel
textStyle TextStyle MaterialTheme.typography.titleMedium Text styling
textColor Color LocalContentColor.current Text color
selectorProperties SelectorProperties Default Selector appearance (shape, color, border)
onSnappedDate (LocalDate) -> Unit {} Callback when date is selected

DateFormatter Options

DateOrder (controls field arrangement):

  • DateOrder.DMY - Day, Month, Year (Europe, most of world)
  • DateOrder.MDY - Month, Day, Year (US)
  • DateOrder.YMD - Year, Month, Day (East Asia, ISO 8601)

MonthDisplayStyle:

  • MonthDisplayStyle.FULL - "January", "February", etc.
  • MonthDisplayStyle.SHORT - "Jan", "Feb", etc.
  • MonthDisplayStyle.NUMERIC - "1", "2", etc.

CjkSuffixConfig (for Chinese/Japanese/Korean):

  • CjkSuffixConfig.ShowAll - Shows year/month/day suffixes (Chinese/Japanese: εΉ΄/月/ζ—₯, Korean: λ…„/μ›”/일)
  • CjkSuffixConfig.HideAll - Hides all suffixes
  • Custom: CjkSuffixConfig(showYearSuffix = true, showMonthSuffix = false, ...)

WheelTimePicker Parameters

Parameter Type Default Description
startTime LocalTime LocalTime.now() Initial selected time
minTime LocalTime LocalTime.MIN Minimum selectable time
maxTime LocalTime LocalTime.MAX Maximum selectable time
timeFormatter TimeFormatter Auto-detected Controls 12/24 hour format (auto: AM/PM for en-US/GB, 24h for others)
size DpSize DpSize(128.dp, 128.dp) Picker dimensions (narrower than date picker)
Other params - Same as WheelDatePicker rowCount, textStyle, textColor, selectorProperties, etc.

TimeFormat:

  • TimeFormat.HOUR_24 - 24-hour format (00:00 - 23:59)
  • TimeFormat.AM_PM - 12-hour format with AM/PM

Setup

datetime-wheel-picker has multiple active versions. The higher one uses the latest versions for Compose, while the others use only stable versions. Choose the one that matches your Compose version, considering this table:

Compose Version datetime-wheel-picker Version
Compose 1.7 badge-version-compose1.7
Compose 1.8 badge-version-compose1.8
Compose 1.9 badge-version-compose1.9
  • Add the Maven Central repository if it is not already there:
repositories {
  mavenCentral()
}
  • In Compose multiplatform projects, add a dependency to the commonMain source set dependencies:
kotlin {
  sourceSets {
    val commonMain by getting {
      dependencies {
        implementation("io.github.darkokoa:datetime-wheel-picker:<version>")
        implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.7.1")
      }
    }
  }
}
  • To use the library in a single-platform project (such as Android project), add a dependency to the dependencies block:
dependencies {
  implementation("io.github.darkokoa:datetime-wheel-picker:<version>")
  implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.7.1")
}
  • If your minimum Android platform's API level (minSdk) < 26, please enable Desugaring like this:
compileOptions {
  isCoreLibraryDesugaringEnabled = true
}

//...

dependencies {
  coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5")
}

License

Released under the Apache License, Version 2.0.

Thx

WheelPickerCompose

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •  

Languages