Working With Time Picker in Jetpack Compose
Time Pickers give the user the option to pick a time or enter one manually. If you need to let users pick a time in your app, this article will show you how to use time pickers in Jetpack Compose.
Time Picker
We can easily create a Time Picker by providing it with the required state, rememberTimePickerState(). We can provide it with the following optional parameters:
initialHour — This is the initial hour for the state, ranging from 0–23
initialMinute — This is the initial minute for the state, ranging from 0–59
is24Hour — If we set this to true, the clock will show the 24 hour format. If set to false, the clock will show the 12 hour format with toggles for AM and PM.
So let’s create a simple time picker as shown below.
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SimpleTimePicker(modifier: Modifier = Modifier) {
val timeState = rememberTimePickerState(
is24Hour = true
)
TimePicker(
modifier = modifier,
state = timeState
)
}
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
TimePickerTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Column(
modifier = Modifier.padding(innerPadding).fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
SimpleTimePicker()
}
}
}
}
}
}
This is what we get.
Time Picker with Dialog
Jetpack compose does not yet provide a time picker with a dialog. But we can easily embed this time picker in a dialog as shown below.
@RequiresApi(Build.VERSION_CODES.O)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TimePickerWithDialog(
modifier: Modifier = Modifier
) {
val timeState = rememberTimePickerState(
is24Hour = true
)
var timeSelected by remember { mutableStateOf("") }
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 = if (timeSelected.isEmpty()) "Select time" else timeSelected,
textAlign = TextAlign.Center,
style = MaterialTheme.typography.headlineMedium
)
if (showDialog) {
Dialog(
onDismissRequest = { showDialog = false },
properties = DialogProperties(usePlatformDefaultWidth = true)
) {
ElevatedCard(
modifier = Modifier
.background(color = MaterialTheme.colorScheme.surface,
shape = MaterialTheme.shapes.extraLarge),
elevation = CardDefaults.cardElevation(defaultElevation = 10.dp),
shape = MaterialTheme.shapes.extraLarge
) {
Column(
modifier = Modifier.padding(16.dp),
) {
Text(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
text = "Select time"
)
TimePicker(
state = timeState,
layoutType = TimePickerLayoutType.Vertical,
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End
) {
Button(
modifier = Modifier.padding(end = 8.dp),
onClick = { showDialog = false }
) {
Text(
text = "Cancel"
)
}
Button(
modifier = Modifier.padding(start = 8.dp),
onClick = {
timeSelected = formattedTime(timeState.hour, timeState.minute)
showDialog = false
}
) {
Text(text = "OK")
}
}
}
}
}
}
}
}
With this code, we have embedded the time picker in a dialog and we can let the user select the time. When we click on the text “Select time,” this is what we get.
And when we click OK, we get this.
Just to make sure that the time is shown in the “HH:mm” format, we can format the selected time using the following code.
@RequiresApi(Build.VERSION_CODES.O)
fun formattedTime(hour: Int, minute: Int): String {
val formatter = DateTimeFormatter.ofPattern("HH:mm")
val time = LocalTime.of(hour, minute).format(formatter)
return time
}
Time Input
If we want users to manually input the time, we simply replace the time picker with the time input composable as shown below.
@RequiresApi(Build.VERSION_CODES.O)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TimePickerInput(
modifier: Modifier = Modifier
) {
val timeState = rememberTimePickerState(
is24Hour = true
)
var timeSelected by remember { mutableStateOf("") }
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 = if (timeSelected.isEmpty()) "Select time" else timeSelected,
textAlign = TextAlign.Center,
style = MaterialTheme.typography.headlineMedium
)
if (showDialog) {
Dialog(
onDismissRequest = { showDialog = false },
properties = DialogProperties(usePlatformDefaultWidth = true)
) {
ElevatedCard(
modifier = Modifier
.background(color = MaterialTheme.colorScheme.surface,
shape = MaterialTheme.shapes.extraLarge),
elevation = CardDefaults.cardElevation(defaultElevation = 10.dp),
shape = MaterialTheme.shapes.extraLarge
) {
Column(
modifier = Modifier.padding(16.dp),
) {
Text(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
text = "Select time"
)
TimeInput(
state = timeState
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End
) {
Button(
modifier = Modifier.padding(end = 8.dp),
onClick = { showDialog = false }
) {
Text(
text = "Cancel"
)
}
Button(
modifier = Modifier.padding(start = 8.dp),
onClick = {
timeSelected = formattedTime(timeState.hour, timeState.minute)
showDialog = false
}
) {
Text(text = "OK")
}
}
}
}
}
}
}
}
We get the following.
TimeInput dialog
As easy as that.
You can find the code here on github.