Working With Date Picker in Jetpack Compose
Date Pickers give the user the option to pick a date or enter one manually. If you need to let users pick a date in your app, this article will show you how to use date pickers in Jetpack Compose.
Date Pickers
We can easily create a Date Picker by simply providing it with the required parameter state. This holds the state of the date picker that will be remembered across recompositions.
Let’s create a date picker as shown below.
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MyDatePicker() {
val dateState = rememberDatePickerState()
DatePicker(
state = dateState
)
}
Then we can use the state to get the date selected. The date selected is stored in millis. So we need to convert the millis to a date object.
This is the code we will use to manipulate the date selected.
import android.os.Build
import androidx.annotation.RequiresApi
import java.time.Instant
import java.time.LocalDate
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.util.Localeclass DateUtils { @RequiresApi(Build.VERSION_CODES.O)
fun convertMillisToLocalDate(millis: Long) : LocalDate {
return Instant
.ofEpochMilli(millis)
.atZone(ZoneId.systemDefault())
.toLocalDate()
} @RequiresApi(Build.VERSION_CODES.O)
fun convertMillisToLocalDateWithFormatter(date: LocalDate, dateTimeFormatter: DateTimeFormatter) : LocalDate {
//Convert the date to a long in millis using a dateformmater
val dateInMillis = LocalDate.parse(date.format(dateTimeFormatter), dateTimeFormatter)
.atStartOfDay(ZoneId.systemDefault())
.toInstant()
.toEpochMilli()
//Convert the millis to a localDate object
return Instant
.ofEpochMilli(dateInMillis)
.atZone(ZoneId.systemDefault())
.toLocalDate()
}
@RequiresApi(Build.VERSION_CODES.O)
fun dateToString(date: LocalDate): String {
val dateFormatter = DateTimeFormatter.ofPattern("EEEE, dd MMMM, yyyy", Locale.getDefault())
val dateInMillis = convertMillisToLocalDateWithFormatter(date, dateFormatter)
return dateFormatter.format(dateInMillis)
}
}
To show you how let us create a text composable below the dialog, and then we will pick a date, which will then be converted to a string.
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ManufacturedDate() {
val dateState = rememberDatePickerState()
val millisToLocalDate = dateState.selectedDateMillis?.let {
DateUtils().convertMillisToLocalDate(it)
}
val dateToString = millisToLocalDate?.let {
DateUtils().dateToString(millisToLocalDate)
} ?: ""
Column {
DatePicker(
state = dateState
)
Text(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
text = dateToString
)
}
}
When we select a date, the date selected is shown as indicated above.
If we want to manually input the date, we simply change showModeToggle to true and we can input our date by simply clicking on the edit icon or pen/pencil like icon shown. We can also change the title and headline of the picker as shown below. Remember thour that when you provide a headline on the picker, the date selected will not be shown on the headline label. Unless the headline you provide is the date selected.
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ManufacturedDate() {
val dateState = rememberDatePickerState()
val millisToLocalDate = dateState.selectedDateMillis?.let {
DateUtils().convertMillisToLocalDate(it)
}
val dateToString = millisToLocalDate?.let {
DateUtils().dateToString(millisToLocalDate)
} ?: ""
Column {
DatePicker(
title = {
Text(text = "Manufactured Date")
},
headline = { Text(text = "Car's manufactured date")},
state = dateState,
showModeToggle = true
)
Text(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
text = dateToString
)
}
}
We can also change the format of the date. If we want the code for a day of the week, we simply provide the pattern to the dateFormatter parameter. So when a user selects a date, the date with a date will be shown on the headline label.
@RequiresApi(Build.VERSION_CODES.O)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ManufacturedDate() {
val dateState = rememberDatePickerState()
val millisToLocalDate = dateState.selectedDateMillis?.let {
DateUtils().convertMillisToLocalDate(it)
}
val dateToString = millisToLocalDate?.let {
DateUtils().dateToString(millisToLocalDate)
} ?: ""
Column {
DatePicker(
dateFormatter = DatePickerFormatter(
selectedDateSkeleton = "EE, dd MMM, yyyy",
),
title = {
Text(text = "Manufactured Date")
},
state = dateState,
showModeToggle = true
)
Text(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
text = dateToString
)
}
}
DatePicker with DatePickerDialog
The recommended way to use a date picker is to embed it in a DatePickerDialog. That gives us more control over it. We can easily show it and dismiss it.
I will create a text composable and make it clickable. Whenever a user clicks on it, the DatePickerDialog with the DatePicker will show up. The user can easily dismiss it by clicking cancel or touching outside the dialog. Here is the code.
@RequiresApi(Build.VERSION_CODES.O)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DatePickerWithDialog(
modifier: Modifier = Modifier
) {
val dateState = rememberDatePickerState()
val millisToLocalDate = dateState.selectedDateMillis?.let {
DateUtils().convertMillisToLocalDate(it)
}
val dateToString = millisToLocalDate?.let {
DateUtils().dateToString(millisToLocalDate)
} ?: "Choose Date"
var showDialog by remember { mutableStateOf(false) }
Column(
modifier = modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = {
showDialog = true
}),
text = dateToString,
textAlign = TextAlign.Center,
style = MaterialTheme.typography.headlineMedium
) if (showDialog) {
DatePickerDialog(
onDismissRequest = { showDialog = false },
confirmButton = {
Button(
onClick = { showDialog = false }
) {
Text(text = "OK")
}
},
dismissButton = {
Button(
onClick = { showDialog = false }
) {
Text(text = "Cancel")
}
}
) {
DatePicker(
state = dateState,
showModeToggle = true
)
}
}
}
}
When we click on the text composable “Choose Date,” this is what we get.
When we press “OK,” this is what we get.
As easy as that.
You can find the code here on github.
And don’t forget to follow me on X.