DatePicker & TimePicker in Compose: Material3 Date/Time Selection
Jetpack Compose Material3 provides elegant date and time selection components that seamlessly integrate into your Android UI. This guide covers the essential patterns for implementing date/time pickers in modern Compose applications.
DatePickerDialog & State Management
The DatePickerDialog composable paired with rememberDatePickerState() provides a controlled approach to date selection:
var showDatePicker by remember { mutableStateOf(false) }
val datePickerState = rememberDatePickerState()
if (showDatePicker) {
DatePickerDialog(
onDismissRequest = { showDatePicker = false },
confirmButton = {
TextButton(onClick = {
val selectedDate = datePickerState.selectedDateMillis
showDatePicker = false
}) {
Text("OK")
}
}
) {
DatePicker(state = datePickerState)
}
}
Button(onClick = { showDatePicker = true }) {
Text("Select Date")
}
SelectableDates for Range Restriction
Control which dates are selectable using the SelectableDates interface:
class DateRangeValidator(
private val minDateMillis: Long,
private val maxDateMillis: Long
) : SelectableDates {
override fun isSelectableDate(utcTimeMillis: Long): Boolean {
return utcTimeMillis >= minDateMillis && utcTimeMillis <= maxDateMillis
}
override fun isSelectableYear(year: Int): Boolean {
return year >= 2024 && year <= 2026
}
}
val datePickerState = rememberDatePickerState(
selectableDates = DateRangeValidator(startMillis, endMillis)
)
DateRangePicker for Range Selection
For selecting a date range, use DateRangePicker:
var showRangePicker by remember { mutableStateOf(false) }
val dateRangePickerState = rememberDateRangePickerState()
if (showRangePicker) {
DatePickerDialog(
onDismissRequest = { showRangePicker = false },
confirmButton = {
TextButton(onClick = {
val startDate = dateRangePickerState.selectedStartDateMillis
val endDate = dateRangePickerState.selectedEndDateMillis
showRangePicker = false
}) {
Text("OK")
}
}
) {
DateRangePicker(state = dateRangePickerState)
}
}
TimePicker: Dial vs Keyboard Input
Material3 offers two time input modes. Choose based on your UX needs:
var selectedTime by remember { mutableStateOf(TimePickerState(10, 30)) }
// Dial input (clock-like interface)
TimePicker(state = selectedTime)
// Keyboard input (manual entry)
// Note: Use TimeInput for keyboard-first experiences
// TimeInput is available via material3-lab extension
Integration with OutlinedTextField
Create a complete date/time picker field:
@Composable
fun DateTimePickerField(
value: LocalDateTime,
onValueChange: (LocalDateTime) -> Unit,
label: String,
modifier: Modifier = Modifier
) {
var showPicker by remember { mutableStateOf(false) }
val datePickerState = rememberDatePickerState(
initialSelectedDateMillis = value.toMillis()
)
OutlinedTextField(
value = value.format(DateTimeFormatter.ofPattern("MMM dd, yyyy")),
onValueChange = {},
label = { Text(label) },
readOnly = true,
trailingIcon = {
IconButton(onClick = { showPicker = true }) {
Icon(Icons.Default.DateRange, contentDescription = null)
}
},
modifier = modifier
)
if (showPicker) {
DatePickerDialog(
onDismissRequest = { showPicker = false },
confirmButton = {
TextButton(onClick = {
datePickerState.selectedDateMillis?.let { mills ->
onValueChange(LocalDateTime.ofInstant(
Instant.ofEpochMilli(mills),
ZoneId.systemDefault()
))
}
showPicker = false
}) {
Text("OK")
}
}
) {
DatePicker(state = datePickerState)
}
}
}
Best Practices
-
State Management: Always use
rememberDatePickerState()to preserve state across recompositions -
Validation: Implement
SelectableDatesfor business logic constraints - User Feedback: Show selected date/time in the UI immediately
- Accessibility: Ensure your picker fields have proper labels and descriptions
- Timezone Awareness: Be explicit about timezone handling when converting timestamps
Material3 date/time pickers provide a modern, Material Design-compliant experience that your users expect.
8 Android App Templates → https://myougatheax.gumroad.com
Top comments (0)