Mobil Geliştirme

Kotlin ile Android: Jetpack Compose Rehberi

Fatih Algül
19.03.2026 231 görüntülenme

Jetpack Compose Nedir?

Jetpack Compose, Google tarafından geliştirilen ve Android kullanıcı arayüzü (UI) oluşturmayı kökten değiştiren modern bir deklaratif UI araç takımıdır. Geleneksel XML tabanlı layout sisteminin yerini alan Compose, tüm arayüzünüzü Kotlin koduyla yazmanıza olanak tanır. Bu yaklaşım sayesinde daha az kod, daha güçlü araçlar ve sezgisel bir Kotlin API ile çalışırsınız.

Geleneksel Android geliştirmede XML dosyalarıyla arayüz tanımlar, ardından findViewById veya View Binding ile bu bileşenlere erişirdiniz. Compose ile bu iki katmanlı yapıya son veriyoruz; her şey tek bir yerde, Kotlin fonksiyonlarında yaşıyor.

Projeye Jetpack Compose Eklemek

Compose kullanmaya başlamak için build.gradle.kts dosyanızda gerekli bağımlılıkları eklemeniz gerekir:

// build.gradle.kts (Module) android { buildFeatures { compose = true } composeOptions { kotlinCompilerExtensionVersion = "1.5.14" } } dependencies { implementation(platform("androidx.compose:compose-bom:2024.06.00")) implementation("androidx.compose.ui:ui") implementation("androidx.compose.material3:material3") implementation("androidx.compose.ui:ui-tooling-preview") implementation("androidx.activity:activity-compose:1.9.0") debugImplementation("androidx.compose.ui:ui-tooling") }

BOM (Bill of Materials) kullanarak tüm Compose kütüphanelerinin uyumlu versiyonlarını tek bir yerden yönetebilirsiniz. Bu, versiyon çakışmalarını önlemenin en pratik yoludur.

Composable Fonksiyonlar: Temel Yapı Taşı

Compose'un temel birimi @Composable anotasyonu ile işaretlenmiş fonksiyonlardır. Bu fonksiyonlar bir UI bileşenini tanımlar ve herhangi bir değer döndürmez; bunun yerine ekranda görüntülenecek içeriği bildirimsel olarak ifade eder.

@Composable fun SelamlamaKarti(isim: String) { Card( modifier = Modifier .fillMaxWidth() .padding(16.dp), elevation = CardDefaults.cardElevation(defaultElevation = 4.dp) ) { Column(modifier = Modifier.padding(16.dp)) { Text( text = "Merhaba, $isim!", style = MaterialTheme.typography.headlineMedium ) Spacer(modifier = Modifier.height(8.dp)) Text( text = "Jetpack Compose dünyasına hoş geldiniz.", style = MaterialTheme.typography.bodyLarge ) } } }

Dikkat ederseniz, XML'de ayrı ayrı tanımlayacağınız CardView, LinearLayout, TextView gibi bileşenlerin hepsi doğrudan Kotlin fonksiyonları olarak ifade ediliyor. Bu yaklaşımın en büyük avantajı, Kotlin'in tüm gücünü — koşullar, döngüler, değişkenler — doğrudan UI kodunuzda kullanabilmenizdir.

State Yönetimi: remember ve mutableStateOf

Compose'da state (durum) yönetimi en kritik konulardan biridir. Compose, state değiştiğinde ilgili bileşenleri yeniden çizer (recomposition). Bu mekanizmayı doğru anlamak, performanslı ve hatasız uygulamalar geliştirmenin anahtarıdır.

@Composable fun SayacEkrani() { var sayac by remember { mutableStateOf(0) } Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { Text( text = "Sayaç: $sayac", style = MaterialTheme.typography.displayMedium ) Spacer(modifier = Modifier.height(16.dp)) Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) { Button(onClick = { sayac++ }) { Text("Artır") } OutlinedButton(onClick = { sayac = 0 }) { Text("Sıfırla") } } } }

Burada birkaç önemli nokta var:

  • remember: Recomposition sırasında değerin korunmasını sağlar. Bu olmadan, her yeniden çizimde değer sıfırlanır.
  • mutableStateOf: Compose'a bu değerin gözlemlenmesi gerektiğini bildirir. Değer değiştiğinde ilgili composable'lar otomatik olarak yeniden çizilir.
  • by delegasyonu: .value yazmak yerine doğrudan değişken gibi kullanmanızı sağlar.

Ekran döndürme gibi yapılandırma değişikliklerinde state'i korumak istiyorsanız rememberSaveable kullanmalısınız:

var sayac by rememberSaveable { mutableStateOf(0) }

Modifier Sistemi: Bileşenleri Şekillendirmek

Modifier, Compose'un en güçlü konseptlerinden biridir. Her bileşenin görünümünü, boyutunu, davranışını ve düzenini kontrol eder. Modifier'lar zincir şeklinde uygulanır ve sıra önemlidir.

@Composable fun StilliButon() { Box( modifier = Modifier .padding(16.dp) // Dış boşluk .clip(RoundedCornerShape(12.dp)) .background(Color(0xFF6200EE)) .clickable { /* tıklama işlemi */ } .padding(horizontal = 24.dp, vertical = 12.dp) // İç boşluk ) { Text( text = "Özel Buton", color = Color.White, fontWeight = FontWeight.Bold ) } }

Modifier sıralamasının sonucu doğrudan etkilediğini unutmayın. Örneğin, paddingbackground ile backgroundpadding tamamen farklı sonuçlar üretir. İlkinde arka plan padding sonrası alanı kaplar; ikincisinde arka plan tüm alanı kaplar ve içerik içe doğru boşluk bırakır.

LazyColumn ile Performanslı Listeler

Compose'da uzun listeler oluşturmak için LazyColumn ve LazyRow kullanılır. Bunlar geleneksel RecyclerView'ın Compose karşılığıdır ve yalnızca ekranda görünen öğeleri oluşturur.

data class Gorev(val id: Int, val baslik: String, val tamamlandi: Boolean) @Composable fun GorevListesi(gorevler: List<Gorev>) { LazyColumn( contentPadding = PaddingValues(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp) ) { items( items = gorevler, key = { it.id } ) { gorev -> GorevSatiri(gorev = gorev) } } } @Composable fun GorevSatiri(gorev: Gorev) { Row( modifier = Modifier .fillMaxWidth() .clip(RoundedCornerShape(8.dp)) .background(MaterialTheme.colorScheme.surfaceVariant) .padding(12.dp), verticalAlignment = Alignment.CenterVertically ) { Checkbox( checked = gorev.tamamlandi, onCheckedChange = null ) Spacer(modifier = Modifier.width(12.dp)) Text( text = gorev.baslik, style = MaterialTheme.typography.bodyLarge, textDecoration = if (gorev.tamamlandi) TextDecoration.LineThrough else TextDecoration.None ) } }

key parametresi performans açısından kritiktir. Compose'un listedeki öğeleri benzersiz şekilde tanımasını sağlar ve gereksiz recomposition'ları önler.

Navigation: Ekranlar Arası Geçiş

Compose'da ekranlar arası navigasyon için Navigation Compose kütüphanesi kullanılır:

@Composable fun UygulamaNavigasyonu() { val navController = rememberNavController() NavHost(navController = navController, startDestination = "ana_sayfa") { composable("ana_sayfa") { AnaSayfaEkrani( onDetayaGit = { id -> navController.navigate("detay/$id") } ) } composable( route = "detay/{ogeId}", arguments = listOf(navArgument("ogeId") { type = NavType.IntType }) ) { backStackEntry -> val ogeId = backStackEntry.arguments?.getInt("ogeId") ?: 0 DetayEkrani(ogeId = ogeId) } } }

Navigasyonda en iyi pratik, event'leri yukarı, state'i aşağı göndermektir (unidirectional data flow). Composable fonksiyonlarınız doğrudan navController'a bağımlı olmamalı; bunun yerine lambda parametreleri aracılığıyla navigasyon tetiklenmelidir.

ViewModel ile Compose Entegrasyonu

Gerçek dünya uygulamalarında state yönetimi için ViewModel kullanmak en sağlıklı yaklaşımdır:

class GorevViewModel : ViewModel() { private val _gorevler = MutableStateFlow<List<Gorev>>(emptyList()) val gorevler: StateFlow<List<Gorev>> = _gorevler.asStateFlow() fun gorevEkle(baslik: String) { val yeniGorev = Gorev( id = _gorevler.value.size + 1, baslik = baslik, tamamlandi = false ) _gorevler.value = _gorevler.value + yeniGorev } fun gorevTamamla(id: Int) { _gorevler.value = _gorevler.value.map { if (it.id == id) it.copy(tamamlandi = !it.tamamlandi) else it } } } @Composable fun GorevEkrani(viewModel: GorevViewModel = viewModel()) { val gorevler by viewModel.gorevler.collectAsStateWithLifecycle() Column { GorevEklemeFormu(onEkle = viewModel::gorevEkle) GorevListesi(gorevler = gorevler) } }

collectAsStateWithLifecycle kullanarak Flow'u yaşam döngüsüne duyarlı şekilde Compose state'ine dönüştürüyoruz. Bu, uygulama arka plana geçtiğinde gereksiz güncellemeleri önler.

Performans İpuçları

  • Stabil tipler kullanın: Compose, parametrelerin değişip değişmediğini kontrol ederek recomposition kararı verir. data class kullanmak bu karşılaştırmayı güvenilir kılar.
  • Lambda referanslarını sabitleyin: onClick = { viewModel.sil(id) } yerine remember ile sararak veya metot referansı kullanarak gereksiz recomposition'ı engelleyin.
  • derivedStateOf kullanın: Bir state'ten türetilen hesaplamalarda derivedStateOf kullanarak yalnızca sonuç değiştiğinde recomposition tetikleyin.
  • LazyColumn'da key belirtin: Liste öğelerinin benzersiz tanımlanmasını sağlayarak diff hesaplamasını optimize edin.
  • Layout Inspector kullanın: Android Studio'nun Layout Inspector aracı ile recomposition sayılarını izleyerek darboğazları tespit edin.

Sonuç

Jetpack Compose, Android UI geliştirmeyi daha hızlı, daha okunabilir ve daha sürdürülebilir hale getiriyor. Deklaratif yapısı sayesinde "ne istediğinizi" tanımlarsınız, "nasıl yapılacağını" framework halleder. XML layout dosyaları, adapter sınıfları ve View Binding gibi ek katmanlardan kurtularak doğrudan Kotlin ile düşünmenize olanak tanır. Yeni bir Android projesine başlıyorsanız Compose ile başlamanız, mevcut bir projeniz varsa yeni ekranlarınızı Compose ile yazarak kademeli geçiş yapmanız önerilir.

Yazar Hakkında
Fatih Algül
TechSoft Solutions
Proje mi var?

Yazılım, IoT veya otomasyon konularında destek almak ister misiniz?

İletişime Geç