Jetpack Compose has revolutionized Android app development with its modern, declarative approach to UI creation. However, while Compose offers built-in components like DatePicker, developers often encounter scenarios where they need to integrate custom functionality, such as adding time selection.
In this blog post, we’ll explore advanced techniques to extend the Jetpack Compose DatePicker by incorporating time selection. This comprehensive guide targets intermediate to advanced Android developers, providing detailed steps, best practices, and reusable code snippets for integrating this feature seamlessly into your Compose application.
Why Combine Date and Time Pickers?
Many apps require both date and time inputs, such as scheduling, reminders, or booking systems. While Jetpack Compose provides a Material DatePicker, it doesn’t include native support for time selection. By combining a DatePicker and TimePicker, you can:
Offer a more intuitive user experience.
Ensure consistency in date and time data handling.
Streamline user workflows by reducing navigation between separate pickers.
Step 1: Setting Up Jetpack Compose Environment
Before diving into implementation, ensure your project is configured for Jetpack Compose:
Add Compose dependencies in your
build.gradle
file:implementation "androidx.compose.material3:material3:1.2.0" implementation "androidx.compose.ui:ui:1.5.0" implementation "androidx.compose.foundation:foundation:1.5.0" implementation "androidx.lifecycle:lifecycle-runtime-compose:2.6.0" implementation "androidx.navigation:navigation-compose:2.6.0"
Set the
minSdkVersion
to 21 or higher in yourbuild.gradle
file.Ensure your app theme supports Material 3 components.
Step 2: Implementing the Custom Date and Time Picker
Jetpack Compose allows us to build composable functions for customized UI elements. Below, we’ll create a unified component that integrates date and time selection.
Composable for Date Selection
Compose provides the DatePickerDialog
from the Material Design library for selecting dates. Here's how to implement it:
@Composable
fun DatePickerDialog(
selectedDate: LocalDate?,
onDateSelected: (LocalDate) -> Unit,
onDismissRequest: () -> Unit
) {
val datePickerState = remember { DatePickerState() }
MaterialDatePicker(
state = datePickerState,
onDismissRequest = onDismissRequest
) {
onDateSelected(it)
}
}
Composable for Time Selection
For time selection, Jetpack Compose doesn’t offer a native TimePicker, but we can build one using foundational components:
@Composable
fun TimePicker(
selectedTime: LocalTime?,
onTimeSelected: (LocalTime) -> Unit
) {
val hour = remember { mutableStateOf(selectedTime?.hour ?: 0) }
val minute = remember { mutableStateOf(selectedTime?.minute ?: 0) }
Column(
modifier = Modifier.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Select Time", style = MaterialTheme.typography.h6)
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
NumberPicker(
value = hour.value,
range = 0..23,
onValueChange = { hour.value = it }
)
Text(":", style = MaterialTheme.typography.h6)
NumberPicker(
value = minute.value,
range = 0..59,
onValueChange = { minute.value = it }
)
}
Button(onClick = {
onTimeSelected(LocalTime.of(hour.value, minute.value))
}) {
Text("OK")
}
}
}
NumberPicker Implementation
NumberPicker
is a custom composable for scrolling numerical values:
@Composable
fun NumberPicker(
value: Int,
range: IntRange,
onValueChange: (Int) -> Unit
) {
LazyColumn(
modifier = Modifier.height(100.dp)
) {
items(range.toList()) { number ->
Text(
text = number.toString(),
modifier = Modifier
.fillMaxWidth()
.clickable { onValueChange(number) },
textAlign = TextAlign.Center
)
}
}
}
Step 3: Combining Date and Time Pickers
To provide a unified interface, create a composable that combines the date and time pickers:
@Composable
fun DateTimePicker(
initialDateTime: Pair<LocalDate, LocalTime>?,
onDateTimeSelected: (LocalDate, LocalTime) -> Unit,
onDismissRequest: () -> Unit
) {
var selectedDate by remember { mutableStateOf(initialDateTime?.first) }
var selectedTime by remember { mutableStateOf(initialDateTime?.second) }
Column(modifier = Modifier.fillMaxSize()) {
if (selectedDate == null) {
DatePickerDialog(
selectedDate = selectedDate,
onDateSelected = { selectedDate = it },
onDismissRequest = onDismissRequest
)
} else {
TimePicker(
selectedTime = selectedTime,
onTimeSelected = {
selectedTime = it
onDateTimeSelected(selectedDate!!, selectedTime!!)
}
)
}
}
}
Step 4: Styling and Theming
Ensure your DateTimePicker aligns with your app’s theme:
Use Material 3 typography and color schemes.
Add padding, margins, and spacing to enhance user experience.
Test for accessibility, including color contrast and screen reader support.
Step 5: Best Practices and Performance Optimization
State Management: Use
remember
andmutableStateOf
for efficient state updates.Reusability: Modularize components for reuse across your app.
Testing: Verify functionality on various screen sizes and Android versions.
User Feedback: Display error messages for invalid inputs or incomplete selections.
Conclusion
Integrating time selection into Jetpack Compose’s DatePicker enhances user experience and fulfills essential app requirements. By following the techniques and best practices outlined in this guide, you can build a polished, professional DateTimePicker for your Compose application.
Leverage the power of Compose to streamline your app’s date and time input workflows. If you found this guide helpful, share it with your fellow developers, and let us know your thoughts in the comments below!