Jetpack Compose'da predictive back hareketini Navigation, ViewModel durumları ve test katmanıyla doğru bağlamayı inceliyoruz. Bu android eğitimi rehberi, iptal edilen jestlerde oluşan hatalı side effect'leri hedefler.
Jetpack Compose'da Predictive Back: Geri Gezinme Hatalarını Önleme
Android eğitimi: Predictive Back'in gerçek yaşam döngüsü
Predictive back, geri jesti tamamlanmadan önce uygulamanın çıkılacak ekranı görsel olarak hazırlamasını sağlar. Kritik ayrım şudur: kullanıcı kenardan sürüklerken geri işlemi henüz onaylanmamıştır. Jest iptal edilirse mevcut ekran ve ViewModel durumu aynen kalmalıdır. Bu nedenle ağ isteği iptali, taslak silme veya Navigation pop işlemini ilerleme karesinde değil, akış normal biçimde tamamlandığında çalıştırın. Bir android eğitim veya android kursu projesinde bunu doğrulamak için geri hareketi yüzde 40 civarında bırakın; ekranın animasyon durumunun sıfırlanması, ancak navigation back stack'in değişmemesi gerekir.
Platform geri çağrıları ile Compose geri çağrıları arasında öncelik sırası vardır. Aynı composable ağacında etkin bir BackHandler, Navigation Compose'un geri davranışını veya daha dıştaki callback'i gölgeleyebilir. Android Studio'nun Layout Inspector aracı callback sırasını göstermez; bu nedenle uygulama modülünde BackHandler ve PredictiveBackHandler kullanımlarını doğrudan taramak daha güvenilirdir:
rg "(PredictiveBackHandler|BackHandler)" app/src
./gradlew :app:lintDebugÖzellikle modal bottom sheet, seçim modu ve tam ekran medya oynatıcı gibi geçici UI katmanları, geri olayını Navigation'dan önce tüketmelidir. Bu katman kapandığında callback'in enabled değeri false olmalı, aksi halde kullanıcı iki kez geri yaptığında beklenmedik ekran pop'ları görür.Jetpack Compose ile iptal edilebilir geri jesti uygulamak
Aşağıdaki örnek, jest ilerlemesini yalnızca görsel kaydırmaya çevirir. onNavigateUp() çağrısı akış başarıyla bittiğinde yapılır; CancellationException ise kullanıcının jesti bıraktığını değil, iptal ettiğini temsil eder. Bu ayrım yapılmazsa kullanıcı geri hareketini iptal etse bile form ekranı kapanabilir. API işaretlemesi kullanılan Activity Compose bağımlılığına göre deneysel olabilir; projedeki import ve opt-in gereksinimini IDE'nin önerdiği sembole göre doğrulayın.
@OptIn(ExperimentalActivityComposeApi::class)
@Composable
fun EditorBackGesture(
hasUnsavedChanges: Boolean,
onNavigateUp: () -> Unit
) {
val offsetX = remember { Animatable(0f) }
PredictiveBackHandler(enabled = hasUnsavedChanges) { progressEvents ->
try {
progressEvents.collect { event ->
// Ekran genisligini Layout ile olcup burada kullanin.
offsetX.snapTo(event.progress * 320f)
}
onNavigateUp() // Yalnizca kullanici geri islemini onayladiysa.
} catch (e: CancellationException) {
offsetX.animateTo(0f)
throw e // Coroutine iptal semantigini korur.
}
}
Box(Modifier.graphicsLayer { translationX = offsetX.value }) {
EditorContent()
}
}graphicsLayer ile translation vermek, layout yeniden olcusumu yerine RenderNode transform uygular. Buna karşılık ilerleme değerini her karede ViewModel'e yazmak, StateFlow emisyonu, reducer ve kalıcı depolama zincirini tetikleyerek geri hareketine ilgisiz iş yükü üretir.Bu örnekteki 320 piksel sabitini üretimde kullanmayın. Container boyutunu BoxWithConstraints ile alıp constraints.maxWidth.toFloat() üzerinden hesaplayın veya hareketi yüzde tabanlı bir alpha/scale dönüşümüne bağlayın. Katlanabilir cihazda ya da çoklu pencere modunda sabit piksel değeri, panelin yalnızca küçük kısmının hareket etmesine neden olur. Jetpack Compose eğitimlerinde sık görülen hata, gesture progress'i mutableStateOf ile ekran seviyesindeki dev UI state'e koymaktır; bu state yalnızca animasyon yapan en dar composable kapsamına ait olmalıdır.
Android MVVM mimarisi içinde geri hareketi side effect'e dönüştürmek
Android MVVM mimarisi kullanan bir formda geri isteği ile kaydetme veya silme gibi kalıcı etkileri ayırın. ViewModel, jestin her ara karesini değil yalnızca kesinleşmiş komutu almalıdır. Aşağıdaki reducer, ekranın kapanma isteğini bir event olarak modeller; composable event'i yalnızca predictive back akışı tamamlanınca yollar.
sealed interface EditorAction {
data object ConfirmBack : EditorAction
}
data class EditorState(
val isDirty: Boolean = false,
val closeRequested: Boolean = false
)
class EditorViewModel : ViewModel() {
private val _state = MutableStateFlow(EditorState())
val state = _state.asStateFlow()
fun dispatch(action: EditorAction) {
when (action) {
EditorAction.ConfirmBack -> _state.update {
if (it.isDirty) it.copy(closeRequested = true) else it
}
}
}
}Ardından closeRequested değerini bir kerelik navigation event'i sanmayın. Process recreation sonrası StateFlow bu değeri tekrar yayınlayabilir ve ekran yeniden kapanabilir. Navigation komutları için Channel veya event ID içeren, tüketimi açıkça modellenmiş bir akış kullanın; kalıcı ekran durumu ile tek seferlik komutu aynı boolean'da birleştirmek yaygın bir hata kaynağıdır.Navigation Compose kendi back stack'ini yönetirken ekranda ayrıca navController.popBackStack() çağıran bir ViewModel tasarlamak, test edilebilirliği düşürür ve çift pop riskini yükseltir. ViewModel sadece NavigateUp niyeti üretsin, composable katmanı bu niyeti NavController'a uygulasın. Bu sınır, kotlin eğitimi ve kotlin kursu içeriklerinde anlatılan structured concurrency kuralıyla da uyumludur: ekranın coroutine scope'u iptal olduğunda geçersiz navigation işi ekranda kalmaz.
Android Studio eğitimi için geri jesti test matrisi
Emülatörde geri davranışını yalnızca geri düğmesine basarak test etmek yeterli değildir; geri düğmesi ilerleme ve iptal yolunu üretmez. Gesture navigation açık bir cihaz profilinde kenardan başlayan, ortada bekleyen ve geri kenarına dönen üç senaryoyu manuel test edin. Hızlı smoke test için bağlı cihazda aşağıdaki komut, ekranın solundan sağa bir sürükleme üretir; koordinatları cihaz çözünürlüğüne göre uyarlayın:
adb shell wm size
adb shell input swipe 1 1200 700 1200 500Bu komut, CI için tek başına kesin bir predictive back doğrulaması değildir; sistem navigasyon modu ve OEM input davranışı sonucu değiştirebilir. CI'da asıl güvenceyi reducer unit testi ve navigation instrumentation testiyle sağlayın.Jest ilerlemesi UI ayrıntısı olduğundan, unit testte doğrudan platform callback'ini taklit etmeye çalışmak yerine kesinleşen komutu test edin. Turbine ile event akışını, Compose UI test ile de iptal sonrası ekranın görünür kaldığını doğrulayabilirsiniz. Örneğin EditorViewModel.dispatch(ConfirmBack) sonrasında beklenen event ID'nin yalnızca bir kez yayıldığını app.cash.turbine:test ile kontrol edin. Android Studio'da testten önce ./gradlew :app:testDebugUnitTest :app:connectedDebugAndroidTest çalıştırmak, emulator testini lokal JVM testinden ayırır. Bu ayrım, mobil uygulama geliştirme eğitimi materyallerinde sıkça atlanan cihaz bağımlı geri jesti ile saf iş kuralını birbirinden izole eder.
Play Store yayınlama öncesi geri davranışı doğrulama
Play Store yayınlama öncesinde release varyantında geri akışını tekrar deneyin. Debug sürümünde etkin olan inspector, fake repository veya farklı shrink ayarları release'te callback zamanlamasını maskeleyebilir. En azından release APK veya bundle'dan yüklenmiş bir yapıda lint ve bağlı cihaz testini ayrı çalıştırın:
./gradlew :app:bundleRelease :app:lintRelease
bundletool build-apks --bundle app/build/outputs/bundle/release/app-release.aab --output /tmp/app.apksArdından taslak düzenleme, modal pencere, derin link ile açılan detay ekranı ve root destination senaryolarında hem tamamlanan hem iptal edilen jesti kontrol edin.Root destination'da predictive back'i zorla tüketmek özellikle risklidir. Back stack boşken uygulamanın sistem tarafından ele alınacak arka plana gitme davranışını uygulama içi popBackStack() ile taklit etmeyin. Bunun yerine callback'i yalnızca gerçekten kapatılacak geçici UI veya geri gidilecek bir destination olduğunda etkinleştirin. Bir android studio eğitimi laboratuvarında bu koşulu logcat ile ölçülebilir hale getirmek için callback etkinleştiğinde destination route ve modal görünürlüğünü yapılandırılmış log olarak yazın; release loglarında kullanıcı verisi veya form içeriği taşımayın.
İlgili Eğitim
YTÜSEM İlgili Eğitim
Sık Sorulan Sorular
Jetpack Compose'da PredictiveBackHandler neden BackHandler ile birlikte çalışmıyor?
İki callback aynı anda etkinse callback önceliği sonucu içteki veya son kaydedilen handler geri olayını tüketebilir. Modal gibi geçici katmanda BackHandler'ı etkin tutun, ekran geçişi için PredictiveBackHandler kullanın ve görünürlük değiştiğinde enabled değerini false yapın. Kod tabanında `rg "BackHandler" app/src` ile çakışan kullanımları bulun.
Android MVVM mimarisi predictive back iptalini ViewModel'e nasıl iletmeli?
İptali domain action olarak iletmeyin; iptal görsel katmanın Animatable değerini sıfırlamalıdır. ViewModel yalnızca progress akışı normal tamamlanınca ConfirmBack gibi kesinleşmiş bir action almalıdır. Böylece jestin yüzde 20, yüzde 60 ve iptal kareleri StateFlow reducer'ını tetiklemez.
Android eğitimi projelerinde predictive back testi adb ile yapılabilir mi?
ADB `input swipe` komutu temel cihaz smoke testi için kullanılabilir, ancak gesture navigation modu ve cihaz imajı sonucu etkiler. İş kurallarını Turbine ile JVM testinde, back stack sonucunu connected Android testte doğrulayın; fiziksel veya gesture navigation etkin emülatörde tamamla ve iptal et senaryolarını ayrıca manuel çalıştırın.
Kotlin kursu projesinde geri jestinde CancellationException neden tekrar fırlatılmalı?
Coroutine iptalini yakalayıp yutmak parent job'ın iptal semantiğini bozar ve alt işlerin beklenenden uzun yaşamasına yol açabilir. Animasyonu sıfırladıktan sonra `throw e` kullanın. Bu sayede Compose scope, iptalin lifecycle kaynaklı mı yoksa kullanıcı jesti kaynaklı mı olduğuna uygun biçimde kapanır.
AI / LLM Discovery
Bu makale Opendart Akademi Android eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.



