Jetpack Compose, Android’s modern UI toolkit, is revolutionizing the way we build user interfaces. Date pickers are a common and crucial UI element in many applications, allowing users to select dates easily and intuitively. In this comprehensive guide, we’ll explore how to implement and customize date pickers in Jetpack Compose, providing you with a practical understanding and numerous code examples.
Understanding DatePickers in Jetpack Compose
Date pickers provide a user-friendly interface for selecting dates. They typically display a calendar-like view, enabling users to navigate through months and years to choose a specific date. With Jetpack Compose, you can easily integrate date pickers into your applications using Material Design components or custom implementations.
Why Use DatePickers?
- Improved User Experience: Simplifies date selection with an intuitive UI.
- Data Accuracy: Reduces errors associated with manual date input.
- Consistent Design: Provides a familiar interface for date selection across different platforms.
Implementing DatePickers in Jetpack Compose
Jetpack Compose offers multiple ways to implement date pickers, including using Material Design components and creating custom date pickers. We’ll start with the simplest approach and gradually move to more complex customizations.
Method 1: Using androidx.compose.material3.DatePicker
The Material 3 DatePicker
is part of the androidx.compose.material3
library. It offers a straightforward and customizable way to integrate date pickers into your Compose UI.
Step 1: Add Dependencies
Ensure that you have the Material 3 library added to your build.gradle
file:
dependencies {
implementation("androidx.compose.material3:material3:1.1.2")
implementation("androidx.compose.material3:material3-window-size-class:1.1.2") //optional if using window size class
implementation("androidx.compose.material:material-icons-extended:1.5.4")
implementation("androidx.compose.runtime:runtime-livedata:1.5.4")
}
Step 2: Basic Implementation
Here’s a simple example of using the DatePicker
composable:
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.DatePicker
import androidx.compose.material3.DatePickerDialog
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.rememberDatePickerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.tooling.preview.Preview
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SimpleDatePicker() {
val openDialog = remember { mutableStateOf(false) }
val date = remember { mutableStateOf("") }
Column(horizontalAlignment = Alignment.CenterHorizontally) {
TextButton(onClick = { openDialog.value = true }) {
Text("Select Date")
}
Text(text = "Selected date: ${date.value}")
}
if (openDialog.value) {
val datePickerState = rememberDatePickerState()
val confirmEnabled = remember {
derivedStateOf { datePickerState.selectedDateMillis != null }
}
DatePickerDialog(
onDismissRequest = { openDialog.value = false },
confirmButton = {
TextButton(
onClick = {
openDialog.value = false
datePickerState.selectedDateMillis?.let { millis ->
date.value = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date(millis))
}
},
enabled = confirmEnabled.value
) {
Text("Confirm")
}
},
dismissButton = {
TextButton(
onClick = {
openDialog.value = false
}
) {
Text("Dismiss")
}
}
) {
DatePicker(state = datePickerState)
}
}
}
@Preview(showBackground = true)
@Composable
fun SimpleDatePickerPreview() {
SimpleDatePicker()
}
In this example:
- A button opens a dialog with a
DatePicker
inside. - The
rememberDatePickerState
stores the selected date. - The dialog shows ‘Confirm’ and ‘Dismiss’ buttons for selecting and cancelling.
- On confirmation, the selected date is formatted and displayed.
Step 3: Configuring the DatePicker State
The rememberDatePickerState
can be customized. It includes options for setting initial dates and date validator.
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.DatePickerState
import androidx.compose.material3.rememberDatePickerState
import androidx.compose.runtime.Composable
import java.time.Instant
import java.time.ZoneId
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ConfiguredDatePickerState(): DatePickerState {
// Define range for valid dates
val startDateMillis = Instant.now().minusMillis(365L * 24 * 3600 * 1000).toEpochMilli() // one year ago
val endDateMillis = Instant.now().plusMillis(365L * 24 * 3600 * 1000).toEpochMilli() // one year ahead
val initialDateMillis = Instant.now().toEpochMilli()
val datePickerState = rememberDatePickerState(
initialSelectedDateMillis = initialDateMillis,
selectableDates = object : SelectableDates {
override fun isSelectableDate(utcTimeMillis: Long): Boolean {
return utcTimeMillis in startDateMillis..endDateMillis
}
}
)
return datePickerState
}
Here, the selectableDates
allow to choose available dates.
Method 2: Using MaterialDatePicker
from the Legacy Support Library
If you prefer to use the legacy MaterialDatePicker
(from the support library), you can integrate it into your Compose application by wrapping it in a ComposeView
.
Step 1: Add Dependencies
Add the Material Components for Android dependency in your build.gradle
file:
dependencies {
implementation("com.google.android.material:material:1.11.0")
}
Step 2: Wrap MaterialDatePicker in ComposeView
Create a ComposeView
that hosts the MaterialDatePicker
. Here’s how:
import android.view.LayoutInflater
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.viewinterop.AndroidView
import androidx.fragment.app.FragmentActivity
import com.google.android.material.datepicker.MaterialDatePicker
import androidx.compose.ui.tooling.preview.Preview
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
@Composable
fun LegacyDatePicker() {
AndroidView({ context ->
ComposeView(context).apply {
setContent {
MaterialDatePickerWrapper()
}
}
})
}
@Composable
fun MaterialDatePickerWrapper() {
val activity = LocalContext.current as? FragmentActivity
val selectedDate = remember { mutableStateOf("") }
val materialDatePicker =
MaterialDatePicker.Builder.datePicker()
.setTitleText("Select date")
.setSelection(MaterialDatePicker.todayInUtcMilliseconds())
.build()
Column {
Button(onClick = {
materialDatePicker.show(activity?.supportFragmentManager!!, "DATE_PICKER")
materialDatePicker.addOnPositiveButtonClickListener { timeInMillis ->
val calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
calendar.timeInMillis = timeInMillis
selectedDate.value = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(calendar.time)
}
}) {
Text("Open Date Picker")
}
Text(text = "Selected Date: ${selectedDate.value}")
}
}
@Preview(showBackground = true)
@Composable
fun LegacyDatePickerPreview() {
LegacyDatePicker()
}
Key points in this implementation:
- We wrap
MaterialDatePicker
usingAndroidView
to make it compatible with Compose. - The
MaterialDatePicker
is shown usingsupportFragmentManager
. - A listener captures the selected date and updates a state variable.
Advanced Customization Techniques
Custom Theming
Customizing the appearance of the date picker ensures that it aligns with your application’s design system. Jetpack Compose allows extensive theming to control colors, fonts, and other visual elements.
import androidx.compose.material3.DatePickerDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProvideTextStyle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.graphics.Color
@Composable
fun ThemedDatePicker() {
MaterialTheme {
// Override the DatePicker's colors
val themedDatePickerColors = DatePickerDefaults.colors(
containerColor = Color.LightGray, // Background color
titleContentColor = Color.Black, // Header text color
yearContentColor = Color.DarkGray // Year picker color
)
val state = rememberDatePickerState()
DatePickerDialog(
onDismissRequest = {},
confirmButton = {},
dismissButton = {}
) {
DatePicker(state = state, colors = themedDatePickerColors)
}
}
}
Conclusion
Jetpack Compose provides multiple options for integrating date pickers into your Android applications. Whether using the straightforward Material 3 DatePicker
or wrapping the legacy MaterialDatePicker
in a ComposeView
, you can easily add this crucial UI component. The flexibility of Jetpack Compose also allows for extensive customization, ensuring that your date pickers align perfectly with your application’s design and branding.