Jetpack Compose’da Form Oluşturma: TextField, Doğrulama ve Gönderim
Modern mobil uygulamaların vazgeçilmez bir parçası olan formlar, kullanıcı etkileşiminin temelini oluşturur. Kullanıcı bilgilerini toplamak, ayarları değiştirmek veya bir işlem başlatmak için formlara ihtiyaç duyarız. Jetpack Compose ile Android UI geliştirme şeklimiz kökten değişti ve form oluşturma süreci de bu yeniliklerden nasibini aldı. Bu makalede, Jetpack Compose kullanarak dinamik, doğrulanabilir ve kullanıcı dostu formları nasıl oluşturacağınızı, TextField bileşeninden başlayarak doğrulama mekanizmalarına ve form gönderimine kadar tüm detaylarıyla ele alacağız.
Jetpack Compose’da Form Geliştirmenin Temelleri
Jetpack Compose, deklaratif bir UI yapısı sunarak form geliştirmeyi daha sezgisel ve yönetilebilir hale getirir. Geleneksel XML tabanlı yaklaşımların aksine, Compose’da UI durumu ve verileri doğrudan kod içinde yönetilir, bu da daha az boilerplate kod ve daha okunabilir bir yapı anlamına gelir.
Neden Jetpack Compose ile Formlar?
Compose’un reaktif yapısı, kullanıcı girişlerine anında tepki vermeyi ve UI’ı otomatik olarak güncellemeyi kolaylaştırır. Bu, özellikle form doğrulama ve hata mesajlarının dinamik olarak gösterilmesi gibi durumlarda büyük avantaj sağlar. Bileşen tabanlı mimarisi sayesinde, form elemanlarını yeniden kullanılabilir parçacıklar halinde tasarlayabiliriz.
Temel Bileşenler: TextField ve Button
Bir formun en temel yapı taşları, kullanıcıdan veri alan TextField ve formu işleme koyan Button bileşenleridir. Compose’da bu bileşenler, özelleştirilebilir parametrelerle gelir ve form ihtiyaçlarınıza göre kolayca şekillendirilebilir. TextField, metin girişi için, Button ise kullanıcı eylemlerini tetiklemek için kullanılır.
Basit Bir Form Yapısı Oluşturma
Jetpack Compose’da basit bir form, genellikle bir Column içinde birden fazla TextField ve bir Button barındırır. Her TextField kendi durumunu (kullanıcının girdiği metin) yönetir ve bu durumlar remember ve mutableStateOf ile Compose’un reaktif sistemine dahil edilir.
@Composable
fun SimpleForm() {
var name by remember { mutableStateOf("") }
var email by remember { mutableStateOf("") }
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
OutlinedTextField(
value = name,
onValueChange = { name = it },
label = { Text("Adınız") },
modifier = Modifier.fillMaxWidth()
)
OutlinedTextField(
value = email,
onValueChange = { email = it },
label = { Text("E-posta Adresiniz") },
modifier = Modifier.fillMaxWidth()
)
Button(
onClick = { /* Formu gönder */ },
modifier = Modifier.fillMaxWidth()
) {
Text("Kaydol")
}
}
}
Bu örnekte, name ve email değişkenleri, TextField'ların değerlerini tutar ve onValueChange ile güncellenir.
TextField Bileşenini Etkin Kullanma
TextField bileşeni, kullanıcıdan metin girişi almak için kullanılan temel bir UI elemanıdır. Ancak sadece metin almakla kalmaz, aynı zamanda birçok özelleştirme ve davranış seçeneği sunar.
TextField Parametreleri ve Özelleştirme
TextField ve OutlinedTextField gibi varyantlar, label, placeholder, leadingIcon, trailingIcon, keyboardOptions, visualTransformation gibi birçok parametreye sahiptir. Bu parametreler sayesinde giriş alanının görünümünü ve davranışını tamamen kontrol edebilirsiniz. Örneğin, bir şifre alanı için visualTransformation kullanarak girilen karakterleri gizleyebilirsiniz.
@Composable
fun CustomizedTextField() {
var password by remember { mutableStateOf("") }
var passwordVisible by remember { mutableStateOf(false) }
OutlinedTextField(
value = password,
onValueChange = { password = it },
label = { Text("Şifre") },
visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
trailingIcon = {
val image = if (passwordVisible)
Icons.Filled.Visibility
else Icons.Filled.VisibilityOff
val description = if (passwordVisible) "Şifreyi gizle" else "Şifreyi göster"
IconButton(onClick = { passwordVisible = !passwordVisible }) {
Icon(imageVector = image, description)
}
},
modifier = Modifier.fillMaxWidth()
)
}
Bu örnek, şifre alanında metni gizleme ve bir görünürlük ikonu ekleme gibi gelişmiş özelleştirmeleri göstermektedir.
Klavye Türleri ve Görsel İpuçları
KeyboardOptions ile klavyenin türünü (sayısal, e-posta, metin vb.) ve imeAction ile klavyedeki "Enter" tuşunun ne yapacağını belirleyebilirsiniz. Bu, kullanıcı deneyimini önemli ölçüde iyileştirir. label ve placeholder gibi görsel ipuçları da kullanıcıya hangi tür verinin beklendiği konusunda rehberlik eder.
Durum Yönetimi: remember ve mutableStateOf
Compose'da TextField'ın değeri, harici bir durum değişkeni tarafından yönetilir. Bu durum değişkeni, remember { mutableStateOf("") } kullanılarak oluşturulur. remember fonksiyonu, Composable'ın yeniden oluşturulması durumunda bile durumun korunmasını sağlarken, mutableStateOf ise durumun değişebilir olduğunu ve değişimlerde UI'ın yeniden çizilmesini tetikleyeceğini belirtir.
Kullanıcı Girişlerini Doğrulama ve Hata Yönetimi
Form doğrulama, uygulamanızın tutarlı ve doğru veriler almasını sağlamak için kritik öneme sahiptir. Kullanıcıya anında geri bildirim vermek, hata oranını düşürür ve kullanıcı deneyimini iyileştirir.
Temel Doğrulama Kuralları (Boşluk Kontrolü, Minimum Uzunluk)
En basit doğrulama, bir alanın boş bırakılıp bırakılmadığını veya belirli bir minimum uzunluğa sahip olup olmadığını kontrol etmektir. Bu tür kontroller genellikle if ifadeleriyle yapılır ve doğrulama hatası durumunda bir boolean değişkeni güncellenir.
@Composable
fun ValidationExample() {
var username by remember { mutableStateOf("") }
var usernameError by remember { mutableStateOf(false) }
Column(modifier = Modifier.padding(16.dp)) {
OutlinedTextField(
value = username,
onValueChange = {
username = it
usernameError = it.isBlank() || it.length < 3
},
label = { Text("Kullanıcı Adı") },
isError = usernameError,
modifier = Modifier.fillMaxWidth()
)
if (usernameError) {
Text(
text = "Kullanıcı adı boş olamaz ve en az 3 karakter olmalı.",
color = MaterialTheme.colors.error,
style = MaterialTheme.typography.caption,
modifier = Modifier.padding(start = 16.dp)
)
}
// ... Diğer alanlar ve buton
}
}
isError parametresi TextField'ın görsel olarak hata durumunda olduğunu belirtir ve genellikle kırmızı bir vurgu ile gösterilir.
Gelişmiş Doğrulama (E-posta, Regex)
Daha karmaşık doğrulamalar için düzenli ifadeler (Regex) kullanmak yaygın bir yöntemdir. Örneğin, bir e-posta adresinin geçerli bir formatta olup olmadığını kontrol etmek için Regex kullanılabilir.
fun isValidEmail(email: String): Boolean {
return android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches()
}
// ...
// onValueChange içinde: emailError = !isValidEmail(it)
Hata Mesajlarını Gösterme
Hata mesajları, kullanıcıya neyin yanlış gittiğini açıkça belirtmelidir. TextField'ın hemen altında Text bileşeni kullanarak hata mesajlarını gösterebilirsiniz. isError parametresiyle birlikte kullanıldığında, bu mesajlar sadece hata durumunda görünür hale gelir.
Jetpack Compose'da Form Durum Yönetimi
Form durumunu etkili bir şekilde yönetmek, özellikle karmaşık formlarda uygulamanın ölçeklenebilirliği ve sürdürülebilirliği için hayati öneme sahiptir.
Basit Durum Yönetimi Yaklaşımları
Yukarıdaki örneklerde gördüğümüz gibi, her TextField için ayrı mutableStateOf değişkenleri tanımlamak, basit formlar için yeterli olabilir. Ancak form büyüdükçe bu yaklaşım kod tekrarına ve yönetilmesi zor bir yapıya yol açabilir.
ViewModel ile Durum Yönetimi
Daha büyük ve karmaşık formlar için ViewModel kullanmak en iyi uygulamalardan biridir. ViewModel, yapılandırma değişikliklerinde (örneğin ekran döndürme) verilerin kaybolmamasını sağlar ve UI'dan iş mantığını ayırır. Bir ViewModel içinde tüm form alanlarının durumunu ve doğrulama mantığını tutabilirsiniz.
class LoginFormViewModel : ViewModel() {
var username by mutableStateOf("")
private set
var password by mutableStateOf("")
private set
var usernameError by mutableStateOf(false)
private set
var passwordError by mutableStateOf(false)
private set
fun updateUsername(newUsername: String) {
username = newUsername
usernameError = newUsername.isBlank() || newUsername.length < 3
}
fun updatePassword(newPassword: String) {
password = newPassword
passwordError = newPassword.isBlank() || newPassword.length < 6
}
fun validateForm(): Boolean {
usernameError = username.isBlank() || username.length < 3
passwordError = password.isBlank() || password.length < 6
return !usernameError && !passwordError
}
fun submitForm() {
if (validateForm()) {
// Formu gönder
println("Form gönderildi: Kullanıcı Adı: $username, Şifre: $password")
}
}
}
@Composable
fun LoginForm(viewModel: LoginFormViewModel = viewModel()) {
Column(modifier = Modifier.padding(16.dp)) {
OutlinedTextField(
value = viewModel.username,
onValueChange = { viewModel.updateUsername(it) },
label = { Text("Kullanıcı Adı") },
isError = viewModel.usernameError,
modifier = Modifier.fillMaxWidth()
)
if (viewModel.usernameError) {
Text("Kullanıcı adı boş olamaz ve en az 3 karakter olmalı.", color = MaterialTheme.colors.error)
}
// ... Şifre alanı ve hata mesajı
Button(
onClick = { viewModel.submitForm() },
enabled = !viewModel.usernameError && !viewModel.passwordError && viewModel.username.isNotBlank() && viewModel.password.isNotBlank(),
modifier = Modifier.fillMaxWidth()
) {
Text("Giriş Yap")
}
}
}
Bu yaklaşım, formun tüm durumunu tek bir yerde toplar ve Composable'ın daha temiz kalmasını sağlar.
Özel Form Durumu Sınıfları Oluşturma
Daha da karmaşık senaryolarda, her bir form alanı için özel bir durum sınıfı (örneğin FormFieldState) oluşturarak doğrulama mantığını ve hata mesajlarını ilgili alanla birleştirebilirsiniz. Bu, özellikle çok sayıda alan ve karmaşık doğrulama kuralları olan formlarda kodun daha modüler olmasını sağlar.
Form Gönderimi ve Veri İşleme
Formun son adımı, kullanıcı tarafından girilen verileri toplamak, doğrulamak ve bir sunucuya göndermek veya yerel olarak işlemek üzere tetiklemektir.
Gönder Butonu ve Etkinleştirme Koşulları
Gönder butonu, genellikle tüm form alanları geçerli olana kadar devre dışı bırakılır. Bu, Button bileşeninin enabled parametresiyle kontrol edilir. Tüm alanların geçerli olup olmadığını kontrol eden bir boolean değişkeni veya ViewModel'daki bir fonksiyon bu koşulu sağlayabilir.
Verilerin Toplanması ve İşlenmesi
Kullanıcı "Gönder" butonuna tıkladığında, ViewModel içindeki ilgili fonksiyon tetiklenir. Bu fonksiyon, form alanlarındaki mevcut değerleri toplar ve gerekli iş mantığını (örneğin bir API çağrısı) yürütür.
Asenkron İşlemler ve Yükleme Durumu
Form gönderimi genellikle bir ağ isteği gibi asenkron bir işlem içerir. Bu tür durumlarda, kullanıcıya işlemin devam ettiğini bildirmek için bir yükleme göstergesi (örneğin CircularProgressIndicator) göstermek önemlidir. ViewModel içinde bir isLoading durumu tutarak bu göstergeyi kontrol edebilirsiniz.
class SubmissionViewModel : ViewModel() {
// ... form alanları ve doğrulama
var isLoading by mutableStateOf(false)
private set
var submissionSuccess by mutableStateOf(null)
private set
fun submitForm() {
if (validateForm()) {
isLoading = true
// Simulate network request
viewModelScope.launch {
delay(2000) // 2 saniye bekle
// API çağrısı sonucuna göre submissionSuccess'i ayarla
submissionSuccess = true // veya false
isLoading = false
}
}
}
}
@Composable
fun SubmissionForm(viewModel: SubmissionViewModel = viewModel()) {
// ... TextField'lar
Button(
onClick = { viewModel.submitForm() },
enabled = !viewModel.isLoading && viewModel.validateForm(), // isLoading durumunu da ekledik
modifier = Modifier.fillMaxWidth()
) {
if (viewModel.isLoading) {
CircularProgressIndicator(color = Color.White, modifier = Modifier.size(24.dp))
} else {
Text("Gönder")
}
}
viewModel.submissionSuccess?.let { success ->
if (success) {
Text("Form başarıyla gönderildi!", color = Color.Green)
} else {
Text("Form gönderilirken bir hata oluştu.", color = Color.Red)
}
}
}
Bu yapı, kullanıcıya akıcı ve bilgilendirici bir deneyim sunar.
Sonuç
Jetpack Compose, Android'de form oluşturma sürecini basitleştiren ve modernleştiren güçlü bir araçtır. TextField bileşeninin esnekliği, reaktif durum yönetimi yetenekleri ve ViewModel ile iş mantığını ayırma imkanı sayesinde, karmaşık formları bile temiz, sürdürülebilir ve test edilebilir bir şekilde geliştirebilirsiniz. Doğrulama ve hata yönetimi, kullanıcı deneyiminin temel taşlarıdır ve Compose bu süreçleri dinamik olarak ele almak için mükemmel bir çerçeve sunar. Bu makaledeki teknikleri uygulayarak, uygulamanız için sağlam ve kullanıcı dostu formlar oluşturmaya başlayabilirsiniz.
Sıkça Sorulan Sorular
Jetpack Compose'da form doğrulama neden önemlidir?
Form doğrulama, uygulamanızın doğru ve geçerli veriler almasını sağlar, veri bütünlüğünü korur ve hatalı girişlerden kaynaklanan sorunları önler. Ayrıca, kullanıcıya anında geri bildirim sağlayarak daha iyi bir kullanıcı deneyimi sunar.
Form durumunu yönetmek için en iyi yöntem nedir?
Basit formlar için remember ve mutableStateOf yeterli olsa da, karmaşık formlarda ViewModel kullanmak en iyi uygulamadır. ViewModel, UI'dan iş mantığını ayırır, yapılandırma değişikliklerinde veri kaybını önler ve test edilebilirliği artırır.
Çok adımlı formlar Jetpack Compose'da nasıl oluşturulur?
Çok adımlı formlar, genellikle her adım için ayrı bir Composable fonksiyonu veya ViewModel içinde bir currentStep durumu tutularak oluşturulur. AnimatedContent veya Crossfade gibi bileşenler, adımlar arasında akıcı geçişler sağlamak için kullanılabilir.
TextField'da klavye türünü nasıl değiştiririm?
TextField bileşeninin keyboardOptions parametresini kullanarak klavye türünü değiştirebilirsiniz. Örneğin, sayısal giriş için KeyboardOptions(keyboardType = KeyboardType.Number) kullanabilirsiniz.
Hata mesajlarını kullanıcıya nasıl gösteririm?
Hata mesajlarını, TextField'ın isError parametresiyle birlikte, genellikle TextField'ın hemen altında bir Text bileşeni kullanarak gösterebilirsiniz. isError true olduğunda, Text bileşeni görünür hale gelir ve hata mesajını gösterir.
