Los selectores de fecha permiten a los usuarios seleccionar una fecha, un período o ambos. Usan un diálogo de calendario o una entrada de texto para permitir que los usuarios seleccionen fechas.
Tipos
Existen tres tipos de selectores de fecha:
- Asegurado: Aparece intercalado dentro del diseño. Es adecuado para diseños compactos en los que un diálogo dedicado podría resultar intrusivo.
- Modal: Aparece como un diálogo que se superpone al contenido de la app. Esto proporciona un enfoque claro en la selección de fechas.
- Entrada modal: Combina un campo de texto con un selector de fecha modal.
Puedes implementar estos selectores de fecha en tu app con los siguientes elementos componibles:
DatePicker
: Elemento componible general para un selector de fecha. El contenedor que use determina si está en la estación de carga o en el modelo.DatePickerDialog
: Es el contenedor de los selectores de fecha de entrada modales y no modales.DateRangePicker
: Para cualquier selector de fecha en el que el usuario puede seleccionar un rango con una fecha de inicio y una de finalización.
State
El parámetro clave que comparten los diferentes elementos componibles del selector de fecha es state
, que toma un objeto DatePickerState
o DateRangePickerState
. Sus propiedades capturan información sobre la selección del usuario con el selector de fecha, como la fecha seleccionada actual.
Para obtener más información sobre cómo puedes usar la fecha seleccionada, consulta la sección Usar la fecha seleccionada.
Selector de fecha acoplado
En el siguiente ejemplo, hay un campo de texto que le solicita al usuario que ingrese su fecha de nacimiento. Cuando hace clic en el ícono de calendario en el campo, se abre un selector de fecha acoplado debajo del campo de entrada.
@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)) }
Puntos clave sobre el código
- El selector de fecha aparece cuando el usuario hace clic en
IconButton
.- El botón de ícono funciona como argumento para el parámetro
trailingIcon
deOutlinedTextField
. - La variable de estado
showDatePicker
controla la visibilidad del selector de fecha acoplado.
- El botón de ícono funciona como argumento para el parámetro
- El contenedor del selector de fecha es un elemento componible
Popup
, que superpone el contenido sin afectar el diseño de otros elementos. selectedDate
captura el valor de la fecha seleccionada del objetoDatePickerState
y le aplica formato con la funciónconvertMillisToDate
.- La fecha seleccionada aparecerá en el campo de texto.
- El selector de fecha acoplado se coloca debajo del campo de texto con un modificador
offset
. - Se usa un
Box
como contenedor raíz para permitir una capa adecuada del campo de texto y el selector de fecha.
Resultados
Después de hacer clic en el ícono de calendario, esta implementación aparece de la siguiente manera:
Selector de fecha modal
Un selector de fecha modal muestra un diálogo que flota sobre la pantalla. Para implementarlo, crea un DatePickerDialog
y pásale un 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) } }
Puntos clave sobre el código
- La función de componibilidad
DatePickerModal
muestra un selector de fecha modal. - La expresión lambda
onDateSelected
se ejecuta cuando el usuario selecciona una fecha.- Expone la fecha seleccionada al elemento componible superior.
- La expresión lambda
onDismiss
se ejecuta cuando el usuario descarta el diálogo.
Resultados
Esta implementación aparece de la siguiente manera:
Selector de fecha modal de entrada
Un selector de fecha modal con entrada muestra un diálogo que flota sobre la pantalla y permite que el usuario ingrese una fecha.
@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) } }
Puntos clave sobre el código
Esto es muy similar al ejemplo de selector de fecha modal. La diferencia principal es la siguiente:
- El parámetro
initialDisplayMode
establece el modo de visualización inicial enDisplayMode.Input
.
Selector de fecha con rango
Puedes crear un selector de fecha que le permita al usuario seleccionar un período entre una fecha de inicio y una de finalización. Para ello, usa DateRangePicker
.
El uso de DateRangePicker
es, en esencia, el mismo que el de DatePicker
. Puedes usarlo para un selector acoplado como elemento secundario de PopUp
, o bien puedes usarlo como un selector modal y pasarlo a DatePickerDialog
. La diferencia principal es que usas DateRangePickerState
en lugar de DatePickerState
.
En el siguiente fragmento, se muestra cómo crear un selector de fecha modal con un rango:
@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) ) } }
Puntos clave sobre el código
- El parámetro
onDateRangeSelected
es una devolución de llamada que recibe unPair<Long?, Long?>
que representa las fechas de inicio y finalización seleccionadas. Esto le otorga al elemento componible superior acceso al rango seleccionado. rememberDateRangePickerState()
crea el estado del selector de rango de fechas.DatePickerDialog
crea un contenedor de diálogo modal.- En el controlador
onClick
del botón de confirmación,onDateRangeSelected
pasa el rango seleccionado al elemento componible superior. - El elemento componible
DateRangePicker
funciona como el contenido del diálogo.
Resultados
Esta implementación aparece de la siguiente manera:
Usar la fecha seleccionada
Para capturar la fecha seleccionada, haz un seguimiento de ella en el elemento componible superior como un Long
y pasa el valor a DatePicker
en onDateSelected
. En el siguiente fragmento, se muestra esto, aunque puedes ver la implementación completa en la app oficial de fragmentos.
// ... 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 } ) } // ...
Básicamente, lo mismo se aplica a los selectores de fecha de período, aunque debes usar un Pair<Long?, Long?>
o una clase de datos para capturar los valores de inicio y finalización.