Tarih seçiciler

Tarih seçiciler, kullanıcıların tarih, tarih aralığı veya her ikisini birden seçmesine olanak tanır. Kullanıcıların tarih seçebilmesi için takvim iletişim kutusu veya metin girişi kullanırlar.

Türler

Üç tür tarih seçici vardır:

  • Yuvaya yerleştirilmiş: Düzen içinde satır içi olarak görünür. Özel bir iletişim kutusunun rahatsız edici olabileceği kompakt düzenler için uygundur.
  • Kalıcı: Uygulama içeriğinin üzerinde bir iletişim kutusu olarak görünür. Bu sayede tarih seçimine odaklanabilirsiniz.
  • Modal giriş: Metin alanını modal tarih seçiciyle birleştirir.

Bu tarih seçicileri, aşağıdaki composable'ları kullanarak uygulamanızda kullanabilirsiniz:

  • DatePicker: Tarih seçici için genel composable. Kullandığınız kapsayıcı, sabitlenmiş mi yoksa model mi olduğunu belirler.
  • DatePickerDialog: Hem kalıcı hem de kalıcı giriş tarih seçicileri için kapsayıcı.
  • DateRangePicker: Kullanıcının başlangıç ve bitiş tarihi olan bir aralık seçebileceği tüm tarih seçiciler için.

Eyalet

Farklı tarih seçici composable'larının ortak olarak paylaştığı temel parametre state parametresidir. Bu parametre, DatePickerState veya DateRangePickerState nesnesi alır. Özellikleri, kullanıcının tarih seçiciyi kullanarak yaptığı seçimle ilgili bilgileri (ör. mevcut seçili tarih) yakalar.

Seçilen tarihten nasıl yararlanabileceğiniz hakkında daha fazla bilgi için Seçilen tarihi kullanma bölümüne bakın.

Sabit tarih seçici

Aşağıdaki örnekte, kullanıcıdan doğum tarihini girmesini isteyen bir metin alanı vardır. Kullanıcılar alandaki takvim simgesini tıkladığında giriş alanının altında sabit bir tarih seçici açılır.

@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))
}

Kodla ilgili önemli noktalar

  • Kullanıcı IconButton simgesini tıkladığında tarih seçici görünür.
    • Simge düğmesi, OutlinedTextField öğesinin trailingIcon parametresi için bağımsız değişken görevi görür.
    • showDatePicker durum değişkeni, sabitlenmiş tarih seçicinin görünürlüğünü kontrol eder.
  • Tarih seçicinin kapsayıcısı, diğer öğelerin düzenini etkilemeden içeriğin üzerine yerleştirilen bir Popup composable'dır.
  • selectedDate, seçilen tarihin değerini DatePickerState nesnesinden alır ve convertMillisToDate işlevini kullanarak biçimlendirir.
  • Seçilen tarih, metin alanında görünür.
  • Sabit tarih seçici, offset değiştiricisi kullanılarak metin alanının altına yerleştirilir.
  • Metin alanının ve tarih seçicinin doğru şekilde katmanlanması için kök kapsayıcı olarak Box kullanılır.

Sonuçlar

Takvim simgesini tıkladıktan sonra bu uygulama aşağıdaki gibi görünür:

Sabit tarih seçici örneği.
Şekil 1. Sabitlenmiş tarih seçici.

Modsal tarih seçici, ekranda yüzen bir iletişim kutusu gösterir. Uygulamak için bir DatePickerDialog oluşturun ve buna bir DatePicker iletin.

@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 composable işlevi, kalıcı bir tarih seçici görüntüler.
  • Kullanıcı bir tarih seçtiğinde onDateSelected lambda ifadesi yürütülür.
    • Seçilen tarihi üst composable'a gösterir.
  • onDismiss lambda ifadesi, kullanıcı iletişim kutusunu kapattığında yürütülür.

Sonuçlar

Bu uygulama aşağıdaki gibi görünür:

Mod penceresi tarih seçici örneği.
Şekil 2. Kalıcı tarih seçici.

Giriş kalıcı tarih seçici

Giriş içeren kalıcı tarih seçici, ekranın üzerinde yüzen ve kullanıcının tarih girmesine olanak tanıyan bir iletişim kutusu gösterir.

@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)
    }
}

Bu, kalıcı tarih seçici örneğine çok benzer. Temel fark şudur:

  • initialDisplayMode parametresi, ilk görüntüleme modunu DisplayMode.Input olarak ayarlar.
Giriş içeren kalıcı tarih seçici.
Şekil 3. Giriş içeren kalıcı tarih seçici.

Aralık içeren tarih seçici

Kullanıcının başlangıç ve bitiş tarihi arasında bir aralık seçmesine olanak tanıyan bir tarih seçici oluşturabilirsiniz. Bunun için DateRangePicker kullanın.

DateRangePicker kullanımı, DatePicker ile temelde aynıdır. PopUp öğesinin alt öğesi olarak sabit seçici için kullanabilir veya kalıcı seçici olarak kullanıp DatePickerDialog öğesine iletebilirsiniz. Temel fark, DatePickerState yerine DateRangePickerState kullanmanızdır.

Aşağıdaki snippet'te, aralık içeren kalıcı bir tarih seçicinin nasıl oluşturulacağı gösterilmektedir:

@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)
        )
    }
}

Kodla ilgili önemli noktalar

  • onDateRangeSelected parametresi, seçilen başlangıç ve bitiş tarihlerini temsil eden bir Pair<Long?, Long?> alan geri çağırmadır. Bu sayede üst composable, seçilen aralığa erişebilir.
  • rememberDateRangePickerState(), tarih aralığı seçici için durumu oluşturur.
  • DatePickerDialog, kalıcı bir iletişim kutusu kapsayıcısı oluşturur.
  • Onay düğmesinin onClick işleyicisinde onDateRangeSelected, seçilen aralığı üst composable'a iletir.
  • DateRangePicker composable, iletişim kutusu içeriği olarak kullanılır.

Sonuçlar

Bu uygulama aşağıdaki gibi görünür:

Modal aralık tarih seçici örneği.
Şekil 4. Seçili aralığı olan kalıcı tarih seçici.

Seçili tarihi kullan

Seçilen tarihi yakalamak için üst composable'da Long olarak izleyin ve değeri onDateSelected içindeki DatePicker parametresine iletin. Aşağıdaki snippet'te bu durum gösterilmektedir ancak tam uygulamayı resmi snippet'ler uygulamasında görebilirsiniz.

// ...
    var selectedDate by remember { mutableStateOf<Long?>(null) }
// ...
        if (selectedDate != null) {
            val date = Date(selectedDate!!)
            val formattedDate = SimpleDateFormat("MMM dd, yyyy", LocalLocale.current.platformLocale).format(date)
            Text("Selected date: $formattedDate")
        } else {
            Text("No date selected")
        }
// ...
        DatePickerModal(
            onDateSelected = {
                selectedDate = it
                showModal = false
            },
            onDismiss = { showModal = false }
        )
    }
// ...

Aralık tarih seçiciler için de temelde aynı durum geçerlidir ancak başlangıç ve bitiş değerlerini yakalamak için Pair<Long?, Long?> veya veri sınıfı kullanmanız gerekir.

Aşağıdaki kaynakları da incelemenizi öneririz: