Выбор даты позволяет пользователям выбирать дату, диапазон дат или и то, и другое. Они используют диалоговое окно календаря или текстовый ввод, чтобы позволить пользователям выбирать даты.
Типы
Существует три типа выбора даты:
- Закреплено : отображается в строке макета. Подходит для компактных макетов, где выделенный диалог может показаться навязчивым.
- Модальный : отображается как диалоговое окно, накладывающееся на содержимое приложения. Это обеспечивает четкий фокус на выборе даты.
- Модальный ввод : объединяет текстовое поле с модальным выбором даты.
Вы можете реализовать эти средства выбора дат в своем приложении, используя следующие компонуемые элементы:
-
DatePicker
: Общий компонуемый для выбора даты. Контейнер, который вы используете, определяет, является ли он пристыкованным или модельным. -
DatePickerDialog
: контейнер для модальных и модальных полей ввода выбора даты. -
DateRangePicker
: для любого выбора даты, где пользователь может выбрать диапазон с начальной и конечной датой.
Состояние
Ключевым параметром, который объединяет различные составные элементы выбора даты, является state
, который принимает объект DatePickerState
или DateRangePickerState
. Их свойства собирают информацию о выборе пользователя с помощью выбора даты, например, текущую выбранную дату.
Дополнительную информацию о том, как можно использовать выбранную дату, см. в разделе Использование выбранной даты .
Закрепленный выбор даты
В следующем примере есть текстовое поле, которое предлагает пользователю ввести дату рождения. Когда он нажимает на значок календаря в поле, под полем ввода открывается закрепленный выбор даты.
@Composable fun DatePickerDocked() { var showDatePicker by remember { mutableStateOf(false) } val datePickerState = rememberDatePickerState() val selectedDate = datePickerState.selectedDateMillis?.let { convertMillisToDate(it) } ?: "" Box( modifier = Modifier.fillMaxWidth() ) { OutlinedTextField( value = selectedDate, onValueChange = { }, label = { Text("DOB") }, readOnly = true, trailingIcon = { IconButton(onClick = { showDatePicker = !showDatePicker }) { Icon( imageVector = Icons.Default.DateRange, contentDescription = "Select date" ) } }, modifier = Modifier .fillMaxWidth() .height(64.dp) ) if (showDatePicker) { Popup( onDismissRequest = { showDatePicker = false }, alignment = Alignment.TopStart ) { Box( modifier = Modifier .fillMaxWidth() .offset(y = 64.dp) .shadow(elevation = 4.dp) .background(MaterialTheme.colorScheme.surface) .padding(16.dp) ) { DatePicker( state = datePickerState, showModeToggle = false ) } } } } } @Composable fun DatePickerFieldToModal(modifier: Modifier = Modifier) { var selectedDate by remember { mutableStateOf<Long?>(null) } var showModal by remember { mutableStateOf(false) } OutlinedTextField( value = selectedDate?.let { convertMillisToDate(it) } ?: "", onValueChange = { }, label = { Text("DOB") }, placeholder = { Text("MM/DD/YYYY") }, trailingIcon = { Icon(Icons.Default.DateRange, contentDescription = "Select date") }, modifier = modifier .fillMaxWidth() .pointerInput(selectedDate) { awaitEachGesture { // Modifier.clickable doesn't work for text fields, so we use Modifier.pointerInput // in the Initial pass to observe events before the text field consumes them // in the Main pass. awaitFirstDown(pass = PointerEventPass.Initial) val upEvent = waitForUpOrCancellation(pass = PointerEventPass.Initial) if (upEvent != null) { showModal = true } } } ) if (showModal) { DatePickerModal( onDateSelected = { selectedDate = it }, onDismiss = { showModal = false } ) } } fun convertMillisToDate(millis: Long): String { val formatter = SimpleDateFormat("MM/dd/yyyy", Locale.getDefault()) return formatter.format(Date(millis)) }
Ключевые моменты кодекса
- Выбор даты появляется, когда пользователь нажимает
IconButton
.- Кнопка со значком служит аргументом для параметра
trailingIcon
объектаOutlinedTextField
. - Переменная состояния
showDatePicker
управляет видимостью закрепленного окна выбора даты.
- Кнопка со значком служит аргументом для параметра
- Контейнер выбора даты представляет собой
Popup
компонуемое окно, которое накладывается на содержимое, не влияя на компоновку других элементов. -
selectedDate
извлекает значение выбранной даты из объектаDatePickerState
и форматирует его с помощью функцииconvertMillisToDate
. - Выбранная дата появится в текстовом поле.
- Закрепленный выбор даты располагается под текстовым полем с помощью модификатора
offset
. - В качестве корневого контейнера используется
Box
, позволяющий правильно расположить текстовое поле и средство выбора даты.
Результаты
После нажатия на значок календаря эта реализация выглядит следующим образом:

Модальный выбор даты
Модальный выбор даты отображает диалог, который плавает по экрану. Чтобы реализовать это, создайте DatePickerDialog
и передайте ему DatePicker
.
@Composable fun DatePickerModal( onDateSelected: (Long?) -> Unit, onDismiss: () -> Unit ) { val datePickerState = rememberDatePickerState() DatePickerDialog( onDismissRequest = onDismiss, confirmButton = { TextButton(onClick = { onDateSelected(datePickerState.selectedDateMillis) onDismiss() }) { Text("OK") } }, dismissButton = { TextButton(onClick = onDismiss) { Text("Cancel") } } ) { DatePicker(state = datePickerState) } }
Ключевые моменты кодекса
- Компонуемая функция
DatePickerModal
отображает модальное окно выбора даты. - Лямбда-выражение
onDateSelected
выполняется, когда пользователь выбирает дату.- Он отображает выбранную дату в родительском компонуемом элементе.
- Лямбда-выражение
onDismiss
выполняется, когда пользователь закрывает диалоговое окно.
Результаты
Эта реализация выглядит следующим образом:

Введите модальный выбор даты
Модальный выбор даты с возможностью ввода отображает диалоговое окно, которое всплывает на экране и позволяет пользователю ввести дату.
@Composable fun DatePickerModalInput( onDateSelected: (Long?) -> Unit, onDismiss: () -> Unit ) { val datePickerState = rememberDatePickerState(initialDisplayMode = DisplayMode.Input) DatePickerDialog( onDismissRequest = onDismiss, confirmButton = { TextButton(onClick = { onDateSelected(datePickerState.selectedDateMillis) onDismiss() }) { Text("OK") } }, dismissButton = { TextButton(onClick = onDismiss) { Text("Cancel") } } ) { DatePicker(state = datePickerState) } }
Ключевые моменты кодекса
Это очень похоже на пример модального выбора даты . Основное отличие заключается в следующем:
- Параметр
initialDisplayMode
устанавливает начальный режим отображения наDisplayMode.Input
.

Выбор даты с диапазоном
Вы можете создать выбор даты, который позволит пользователю выбрать диапазон между начальной и конечной датой. Для этого используйте DateRangePicker
.
Использование DateRangePicker
по сути такое же, как и DatePicker
. Вы можете использовать его для закрепленного выборщика как дочернего элемента PopUp
, или вы можете использовать его как модальный выборщик и передать его DatePickerDialog
. Основное отличие в том, что вы используете DateRangePickerState
вместо DatePickerState
.
В следующем фрагменте показано, как создать модальный выбор даты с диапазоном:
@Composable fun DateRangePickerModal( onDateRangeSelected: (Pair<Long?, Long?>) -> Unit, onDismiss: () -> Unit ) { val dateRangePickerState = rememberDateRangePickerState() DatePickerDialog( onDismissRequest = onDismiss, confirmButton = { TextButton( onClick = { onDateRangeSelected( Pair( dateRangePickerState.selectedStartDateMillis, dateRangePickerState.selectedEndDateMillis ) ) onDismiss() } ) { Text("OK") } }, dismissButton = { TextButton(onClick = onDismiss) { Text("Cancel") } } ) { DateRangePicker( state = dateRangePickerState, title = { Text( text = "Select date range" ) }, showModeToggle = false, modifier = Modifier .fillMaxWidth() .height(500.dp) .padding(16.dp) ) } }
Ключевые моменты кодекса
- Параметр
onDateRangeSelected
— это обратный вызов, который получаетPair<Long?, Long?>
, представляющий выбранные начальную и конечную даты. Это дает родителю составной доступ к выбранному диапазону. -
rememberDateRangePickerState()
создает состояние для выбора диапазона дат. -
DatePickerDialog
создает контейнер модального диалогового окна. - В обработчике
onClick
кнопки подтвержденияonDateRangeSelected
передает выбранный диапазон родительскому компонуемому элементу. - Содержимое диалогового окна — составной элемент
DateRangePicker
.
Результаты
Эта реализация выглядит следующим образом:

Использовать выбранную дату
Чтобы захватить выбранную дату, отслеживайте ее в родительском компонуемом как Long
и передайте значение в DatePicker
в onDateSelected
. Следующий фрагмент демонстрирует это, хотя вы можете увидеть полную реализацию в официальном приложении snippets .
// ... var selectedDate by remember { mutableStateOf<Long?>(null) } // ... if (selectedDate != null) { val date = Date(selectedDate!!) val formattedDate = SimpleDateFormat("MMM dd, yyyy", Locale.getDefault()).format(date) Text("Selected date: $formattedDate") } else { Text("No date selected") } // ... DatePickerModal( onDateSelected = { selectedDate = it showModal = false }, onDismiss = { showModal = false } ) } // ...
По сути, то же самое применимо к выборам диапазонов дат , хотя для захвата начальных и конечных значений вам необходимо использовать Pair<Long?, Long?>
или класс данных.