Skip to content

Instantly share code, notes, and snippets.

@tpoisson
Last active April 11, 2024 09:05
Show Gist options
  • Save tpoisson/83c93dae9a96ea38a12761051bc2e60c to your computer and use it in GitHub Desktop.
Save tpoisson/83c93dae9a96ea38a12761051bc2e60c to your computer and use it in GitHub Desktop.
DatePicker for Jetpack Compose and Material
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DatePickerWithDialog(
value: LocalDate?,
dateFormatter: (LocalDate) -> String,
enabled: Boolean = true,
placeholder: @Composable (() -> Unit)? = null,
dateValidator: (Long) -> Boolean = { true },
onChange: (LocalDate?) -> Unit
) {
var openDialog by remember { mutableStateOf(false) }
val datePickerState = rememberDatePickerState(
initialSelectedDateMillis = value?.atStartOfDay()?.toEpochSecond(ZoneOffset.UTC)
?.times(1000)
)
Box {
TextField(
value = value?.let(dateFormatter).orEmpty(),
onValueChange = {},
readOnly = true,
enabled = enabled,
label = placeholder ?: { DatePickerDefaults.DatePickerTitle(datePickerState) },
colors = TextFieldDefaults.textFieldColors(),
trailingIcon = {
Icon(
Icons.Default.EditCalendar,
contentDescription = ""
)
})
Box(
Modifier
.clickable(enabled = enabled) { openDialog = true }
.matchParentSize()) {
// This Box goes over the TextField allowing it to mask it
}
}
if (openDialog) {
val confirmEnabled by remember { derivedStateOf { datePickerState.selectedDateMillis != null } }
DatePickerDialog(
onDismissRequest = {
// Dismiss the dialog when the user clicks outside the dialog or on the back
// button. If you want to disable that functionality, simply use an empty
// onDismissRequest.
openDialog = false
},
confirmButton = {
TextButton(
onClick = {
openDialog = false
onChange(datePickerState.selectedDateMillis?.let {
Instant.ofEpochMilli(it).atZone(ZoneId.systemDefault()).toLocalDate()
})
},
enabled = enabled && confirmEnabled
) {
Text(stringResource(id = android.R.string.ok))
}
},
dismissButton = {
TextButton(
onClick = {
openDialog = false
}
) {
Text(stringResource(id = android.R.string.cancel))
}
}
) {
DatePicker(
state = datePickerState,
dateValidator = dateValidator,
)
}
}
}
@Preview(showBackground = true)
@Composable
private fun DatePickerPreview() {
val locale = ConfigurationCompat.getLocales(LocalConfiguration.current)[0]
Column(
modifier = Modifier.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
DatePickerWithDialog(
dateFormatter = {
DateTimeFormatter.ofPattern("yyyy MMM dd", locale).format(it)
},
value = LocalDate.of(2022, 10, 20),
placeholder = { Text("Custom placeholder") }) {
}
DatePickerWithDialog(
dateFormatter = {
DateTimeFormatter.ofPattern("yyyy MMM dd", locale).format(it)
},
value = LocalDate.of(2022, 10, 20)
) {
}
DatePickerWithDialog(
enabled = false,
dateFormatter = { it.toString() },
value = LocalDate.of(2022, 10, 20),
placeholder = {
Text(stringResource(id = androidx.compose.material3.R.string.date_input_title))
}) {
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment