
If you are building an Android app with Jetpack Compose and you need users to pick a time, the Jetpack Compose TimePicker composable is the tool built for exactly that job. It ships with Compose Material3 and gives you a clean, tappable clock dial that Android users already recognize from stock system apps. Instead of hand-rolling your own hour and minute spinners, you can drop in the Jetpack Compose TimePicker composable and get a polished, accessible time selection surface in just a few lines of code.
Whether you are building a reminder screen, a scheduling form, or an alarm feature, the Jetpack Compose TimePicker handles the interaction logic for you. Your job is simply to read the selected hour and minute out of its TimePicker in Jetpack Compose state and wire that into the rest of your app. This guide walks through every part of the Jetpack Compose TimePicker, from setup to full customization, using the Compose Material3 API.
Google distributes Jetpack's TimePicker as part of the wider Jetpack collection of Android libraries, and Compose is the declarative UI toolkit that renders the TimePicker in Jetpack Compose on screen. By the end of this Jetpack Compose TimePicker walkthrough you will know how to set it up, style it, and read back the time a user selects.
The TimePicker in Jetpack Compose lives in the androidx.compose.material3 package and renders an analog clock dial for time selection, matching Material Design 3 time selection patterns. When you use the Jetpack Compose TimePicker, users drag the clock hand or tap a number to choose an hour, then repeat the gesture for the minute.
The TimePicker in Jetpack Compose Material3 supports both 12-hour and 24-hour clock format, so the same Jetpack's TimePicker works for locales and apps that prefer either style. The Jetpack Compose TimePicker is currently marked as an experimental Material3 API, which means you opt in explicitly before using it in your project.
A few defining characteristics of the TimePicker in Jetpack Compose are worth calling out before you start writing code:
Because the TimePicker in Jetpack Compose is stateless, nothing forces you to display it full screen. Later in this article you will see Jetpack's TimePicker wrapped inside a dialog, which is the most common way apps present a Jetpack Compose TimePicker to end users.
Before you can use the TimePicker in Jetpack Compose, your project needs the Compose Material3 dependency. Most Jetpack Compose projects already include it through the Compose bill of materials, but if you see an unresolved reference to Jetpack's TimePicker, add the material3 artifact explicitly to your module's build file.
Because the Jetpack Compose TimePicker is still an experimental Material3 API, every function that calls it needs to opt in with the ExperimentalMaterial3Api annotation. You can either annotate the individual Jetpack's TimePicker function or opt in at the file level. This small bit of setup is the only extra step a TimePicker in Jetpack Compose screen needs beyond a normal composable.
// build.gradle.kts (Module :app)
dependencies {
implementation("androidx.compose.material3:material3:1.2.1")
implementation("androidx.compose.ui:ui:1.6.7")
}
import androidx.compose.material3.*
import androidx.compose.runtime.*
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TimePickerSetupDemo() {
val timePickerState = rememberTimePickerState(
initialHour = 9,
initialMinute = 30,
is24Hour = false
)
TimePicker(state = timePickerState)
}
With the dependency in place and the opt-in annotation applied, the Jetpack Compose TimePicker composable is ready to render on screen. The next step is understanding the TimePicker in Jetpack Compose state object that drives it.
TimePickerState is the object that holds everything the Jetpack Compose TimePicker needs: the selected hour, the selected minute, and whether the clock is running in 24-hour format. You create it with the rememberTimePickerState function, which survives recomposition just like other remembered Jetpack Compose state.
Jetpack Compose developers rely on this same state pattern across many Material3 components, not only the Jetpack's TimePicker, so the rememberTimePickerState approach feels familiar once you know it. The rememberTimePickerState function accepts three parameters, all of which are optional with sensible defaults:
| Parameter | Type | Purpose |
|---|---|---|
| initialHour | Int | The hour TimePicker starts on, 0 to 23 |
| initialMinute | Int | The minute TimePicker starts on, 0 to 59 |
| is24Hour | Boolean | Whether the dial uses 24-hour clock format |
Once you have a TimePickerState instance, you pass it straight into the TimePicker in Jetpack Compose, and Jetpack Compose keeps the dial and the state object in sync automatically. Any drag or tap on the clock face updates the underlying TimePickerState, and you read that updated value whenever you need it, such as when a user taps a confirm button on your Jetpack Compose TimePicker screen.
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.foundation.layout.Column
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TimePickerStateDemo() {
val timePickerState = rememberTimePickerState(
initialHour = 14,
initialMinute = 45,
is24Hour = true
)
Column {
TimePicker(state = timePickerState)
Text(text = "Selected: ${timePickerState.hour}:${timePickerState.minute}")
}
}
This code creates a TimePickerState with an initial hour of 14 and minute of 45, then passes it to the TimePicker in Jetpack Compose. The Text composable below the dial reads the live hour and minute directly from timePickerState, so the label updates the moment the user moves the clock hand on the Jetpack Compose TimePicker.
Putting a TimePicker in Jetpack Compose on screen only takes two pieces: a remembered TimePickerState and Jetpack's TimePicker composable itself. There is no required callback and no confirm button built into the Jetpack Compose TimePicker, since its only job is to display the dial and keep its state updated.
A basic TimePicker in Jetpack Compose implementation typically sits inside a Column or Box so you can add supporting text or buttons around it. Because Jetpack Compose manages the TimePicker's own internal layout, you do not need to size or arrange the clock face and the period selector yourself.
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BasicTimePickerScreen() {
val timePickerState = rememberTimePickerState()
Column(modifier = Modifier.padding(24.dp)) {
Text(text = "Pick a time", style = MaterialTheme.typography.titleMedium)
TimePicker(state = timePickerState)
}
}
This basic Jetpack Compose TimePicker screen shows a title followed by the clock dial. Since no initial hour or minute was passed to rememberTimePickerState, the TimePicker in Jetpack Compose starts on the current system time by default, which is convenient for most scheduling screens.
One of the most common questions when using the Jetpack Compose TimePicker is how to control whether it shows a 12-hour clock with AM and PM, or a straight 24-hour clock format. The answer is the is24Hour parameter on rememberTimePickerState.
When is24Hour is set to false, the TimePicker in Jetpack Compose renders an extra period selector next to the clock dial so the user can toggle between AM and PM. When is24Hour is true, that selector disappears and the dial simply counts from 0 through 23.
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.*
import androidx.compose.runtime.*
import java.text.DateFormat
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun FormatAwareTimePicker() {
val uses24HourFormat = DateFormat.is24HourFormat(java.util.Locale.getDefault())
val timePickerState = rememberTimePickerState(
initialHour = 8,
initialMinute = 0,
is24Hour = uses24HourFormat
)
Column {
TimePicker(state = timePickerState)
}
}
Here, the code checks the device locale with DateFormat.is24HourFormat and feeds that boolean directly into rememberTimePickerState. This way the Jetpack Compose TimePicker automatically matches whatever clock format the rest of the phone is already using, so your TimePicker in Jetpack Compose screen never looks out of place.
The Jetpack Compose TimePicker can arrange its clock dial and its hour and minute display in two different layouts, controlled by the layoutType parameter. This is exposed through the TimePickerLayoutType enum Jetpack ships, which offers Vertical and Horizontal options for the TimePicker in Jetpack Compose.
import androidx.compose.material3.*
import androidx.compose.runtime.*
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HorizontalTimePicker() {
val timePickerState = rememberTimePickerState()
TimePicker(
state = timePickerState,
layoutType = TimePickerLayoutType.Horizontal
)
}
This example forces the Jetpack Compose TimePicker into the Horizontal arrangement regardless of screen size, which can be useful when the dial sits inside a fixed-width dialog. Passing TimePickerDefaults.layoutType() instead would let the TimePicker in Jetpack Compose decide the arrangement for you. Both layouts are still the same Jetpack Compose TimePicker underneath, only the arrangement of the clock dial and time display changes.
Every visual piece of the TimePicker in Jetpack Compose clock dial can be restyled through TimePickerDefaults.colors(). This function accepts a long list of optional color parameters, and any you skip simply fall back to your MaterialTheme values.
Some of the most useful color parameters Jetpack exposes on TimePickerDefaults.colors() include:
Theming the TimePicker in Jetpack Compose through TimePickerDefaults.colors() keeps your clock dial consistent with the rest of your Material3 screens.
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.Color
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ColoredTimePicker() {
val timePickerState = rememberTimePickerState()
TimePicker(
state = timePickerState,
colors = TimePickerDefaults.colors(
clockDialColor = Color(0xFFE8F0FE),
selectorColor = Color(0xFF3F51B5),
containerColor = Color.White
)
)
}
This snippet overrides just three color parameters on the Jetpack Compose TimePicker, giving the dial a light blue background with an indigo selector. Every parameter left out of the colors() call keeps using the values that come from your app's MaterialTheme.
Once a user finishes interacting with the TimePicker in Jetpack Compose, you need to pull the chosen hour and minute back out of TimePickerState. Both values are plain integer properties on the state object, a pattern Jetpack keeps consistent, so reading them from your Jetpack Compose TimePicker is straightforward.
The hour property always returns a value in 24-hour clock format internally, even when the dial is displayed in 12-hour mode with an AM and PM selector. If you need to show the time in 12-hour format for a label, you convert it yourself before formatting.
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.*
import androidx.compose.runtime.*
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ReadSelectedTime() {
val timePickerState = rememberTimePickerState()
var summary by remember { mutableStateOf("No time chosen yet") }
Column {
TimePicker(state = timePickerState)
Button(onClick = {
val hour = timePickerState.hour
val minute = timePickerState.minute
summary = "You picked %02d:%02d".format(hour, minute)
}) {
Text("Confirm time")
}
Text(text = summary)
}
}
Tapping the confirm button in this example reads timePickerState.hour and timePickerState.minute at that exact moment and formats them into a readable string. This pattern, reading TimePicker in Jetpack Compose state only when the user confirms, is common because it avoids updating other parts of your UI on every single drag gesture.
In real Android apps, the Jetpack Compose TimePicker rarely sits directly on a screen. Instead, the TimePicker in Jetpack Compose is almost always wrapped in a dialog that the user opens from a button or a text field, picks a time in, then confirms or cancels.
Building a Jetpack Compose TimePicker dialog is a great practical example because it combines everything covered so far: TimePickerState, Jetpack's TimePicker composable, and reading the final value on confirm. The AlertDialog composable Jetpack provides through Material3 gives your TimePicker in Jetpack Compose dialog the confirm and dismiss buttons for free.
import androidx.compose.material3.*
import androidx.compose.runtime.*
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TimePickerDialogDemo() {
var showDialog by remember { mutableStateOf(false) }
var chosenTime by remember { mutableStateOf("No time selected") }
val timePickerState = rememberTimePickerState(is24Hour = true)
Button(onClick = { showDialog = true }) {
Text("Open time picker")
}
Text(text = chosenTime)
if (showDialog) {
AlertDialog(
onDismissRequest = { showDialog = false },
confirmButton = {
TextButton(onClick = {
chosenTime = "%02d:%02d".format(timePickerState.hour, timePickerState.minute)
showDialog = false
}) { Text("OK") }
},
dismissButton = {
TextButton(onClick = { showDialog = false }) { Text("Cancel") }
},
text = { TimePicker(state = timePickerState) }
)
}
}
This Jetpack Compose TimePicker dialog example opens on a button tap, shows the clock dial inside an AlertDialog, and only commits the chosen hour and minute when the user presses OK. Dismissing the dialog leaves the previously chosen time untouched, which matches how most production TimePicker in Jetpack Compose screens are expected to behave, since a full-screen clock face rarely fits a compact scheduling form.
Here is one complete, self-contained Jetpack Compose TimePicker screen that ties every piece covered above together: dependency setup, TimePickerState, 24-hour format, custom colors, and a confirm button that reads the final selection from the TimePicker in Jetpack Compose.
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CompleteTimePickerExample() {
val timePickerState = rememberTimePickerState(
initialHour = 10,
initialMinute = 15,
is24Hour = true
)
var confirmedTime by remember { mutableStateOf("No time confirmed yet") }
Column(
modifier = Modifier.padding(24.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Text(text = "Jetpack Compose TimePicker", style = MaterialTheme.typography.titleLarge)
TimePicker(
state = timePickerState,
colors = TimePickerDefaults.colors(
clockDialColor = Color(0xFFEFEFFB),
selectorColor = Color(0xFF6750A4)
)
)
Button(onClick = {
confirmedTime = "%02d:%02d".format(timePickerState.hour, timePickerState.minute)
}) {
Text("Confirm")
}
Text(text = confirmedTime)
}
}