Skip to content

Add cross-platform DatePicker composable (commonMain) #6814

@michaelabon

Description

@michaelabon

Context

Per this comment, opening an issue so we can talk about the approach to add a cross-platform DatePicker composable in commonMain.

Two places currently use Android-only DatePickerDialog:

  • MarkCompletedConstructionForm (construction quest opening date)
  • DateTimeSelectField (log filtering screen)

Proposed approach

Two new files in commonMain, then wiring the component into both call sites above.

1. DateFormatElements.kt (util/locale/)

Locale-aware detection of date component ordering (DMY, MDY, YMD) and separator character, following the same approach as TimeFormatElements.kt. Formats a known reference date with the locale's short date formatter, then finds the positions of year, month, and day in the output to determine order.

2. DatePicker.kt (ui/common/)

A DatePicker composable using three WheelPickers (year, month, day), ordered according to the locale's DateFormatElements. Month wheel shows localized abbreviated month names.

State exposed via DatePickerState with a date: LocalDate property, created through rememberDatePickerState(initialDate, years).

Day clamping when month/year changes

The WheelPicker's items list is passed directly to a LazyColumn, so if the day list shrinks (e.g. 31 → 28) while day 31 is selected, the DatePicker must handle this explicitly.

The plan is to follow the convention used by both iOS UIDatePicker and Android's DatePicker (spinner mode):

  • The day list updates immediately as the month wheel scrolls (not waiting for it to settle).
  • If the selected day exceeds the new month's max, the day wheel animates to the new last day (e.g. 31 → 28 for February).
  • The original day is not restored if you scroll back to a month that supports it.

Both platform pickers work this way (AOSP's DatePickerSpinnerDelegate recalculates maxValue on change; iOS's UIDatePicker clamps identically). This avoids invalid intermediate states where the user could see or submit "February 31".

In terms of implementation, a LaunchedEffect on month + year would call animateScrollToItem on the day WheelPickerState whenever the day count shrinks below the currently selected day.

Wiring it in

The PR would replace the Android DatePickerDialog in both MarkCompletedConstructionForm and DateTimeSelectField so we can verify the component works end-to-end. I'll include before/after screenshots in the PR.

As always, I'm happy for any feedback, edits, or pushback you may have on this approach!

Metadata

Metadata

Assignees

No one assigned

    Labels

    iOSnecessary for iOS port

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions