Compose kullanıcı arayüzünüzün mimarisini oluşturma

Compose'da kullanıcı arayüzü sabittir. Çizildikten sonra bu arayüzün güncellenmesi mümkün değildir. Kullanıcı arayüzünüzün durumunu kontrol edebilirsiniz. Kullanıcı arayüzünün durumu her değiştiğinde, Compose kullanıcı arayüzü ağacının değişen bölümlerini yeniden oluşturur. Oluşturulabilir öğeler, durumu kabul edebilir ve etkinlikleri gösterebilir. Örneğin, TextField bir değeri kabul eder ve geri çağırma işleyicinin değeri değiştirmesini isteyen bir geri çağırma onValueChange gösterir.

var name by remember { mutableStateOf("") }
OutlinedTextField(
    value = name,
    onValueChange = { name = it },
    label = { Text("Name") }
)

Oluşturulabilir öğeler durumu kabul ettiğinden ve etkinlikleri açığa çıkardığından tek yönlü veri akışı kalıbı Jetpack Compose'a uygundur. Bu kılavuz, Compose'da tek yönlü veri akışı kalıbının nasıl uygulanacağına, etkinliklerin ve durum sahiplerinin nasıl uygulanacağına ve Compose'da ViewModels ile nasıl çalışabileceğinize odaklanmaktadır.

Tek yönlü veri akışı

Tek yönlü veri akışı (UDF), durumun aşağı doğru indiği ve etkinliklerin yukarı çıktığı bir tasarım kalıbıdır. Tek yönlü veri akışını uygulayarak kullanıcı arayüzünde durumu görüntüleyen besteleri, uygulamanızın durumu depolayan ve değiştiren bölümlerinden ayırabilirsiniz.

Tek yönlü veri akışı kullanan bir uygulama için kullanıcı arayüzü güncelleme döngüsü şöyle görünür:

  • Etkinlik: Kullanıcı arayüzünün bir bölümü, bir etkinlik oluşturur ve bunu, işlemesi için ViewModel'e iletilen bir düğme tıklaması gibi yukarı doğru iletir veya uygulamanızın diğer katmanlarından (örneğin, kullanıcı oturumunun sona erdiğini belirten) bir etkinlik aktarılır.
  • Güncelleme durumu: Bir etkinlik işleyici durumu değiştirebilir.
  • Görüntüleme durumu: Durum tutucu durumu iletir ve kullanıcı arayüzü bunu görüntüler.

Şekil 1. Tek yönlü veri akışı.

Jetpack Compose'u kullanırken bu kalıbı izlemek çeşitli avantajlar sağlar:

  • Test edilebilirlik: Durumun görüntülenen kullanıcı arayüzünden ayrıştırılması, her ikisinin de yalıtımda test edilmesini kolaylaştırır.
  • Eyalet kapsülleme: Durum yalnızca tek bir yerde güncellenebileceği ve bir bestenin durumu için tek bir doğruluk kaynağı olduğu için tutarsız durumlar nedeniyle hata oluşturma olasılığınız daha düşüktür.
  • Kullanıcı arayüzü tutarlılığı: Tüm durum güncellemeleri StateFlow veya LiveData gibi gözlemlenebilir durum sahipleri kullanılarak kullanıcı arayüzüne anında yansıtılır.

Jetpack Compose'da tek yönlü veri akışı

Oluşturulabilir içerikler, duruma ve olaylara dayalı olarak çalışır. Örneğin, bir TextField yalnızca value parametresi güncellendiğinde güncellenir ve bir onValueChange geri çağırması (değerin yeni değerle değiştirilmesini isteyen bir etkinlik) gösterir. Compose, State nesnesini bir değer sahibi olarak tanımlar ve durum değerinde yapılan değişiklikler yeniden oluşturma işlemini tetikler. Değerini ne kadar süreyle hatırlamanız gerektiğine bağlı olarak durumu remember { mutableStateOf(value) } veya rememberSaveable { mutableStateOf(value) içinde tutabilirsiniz.

TextField composable öğesinin değerinin türü String olduğundan bu değer, koda gömülmüş bir değerden, ViewModel'den veya üst composable öğeden iletilen herhangi bir yerden gelebilir. Bu değeri bir State nesnesinde tutmanız gerekmez ancak onValueChange çağrıldığında değeri güncellemeniz gerekir.

Oluşturulabilir parametreleri tanımlama

Oluşturulabilecek bir öğenin durum parametrelerini tanımlarken aşağıdaki soruları aklınızda bulundurmalısınız:

  • Ne kadar yeniden kullanılabilir veya esnektir?
  • Durum parametreleri bu beste öğesinin performansını nasıl etkiler?

Ayrıştırmayı ve yeniden kullanımı teşvik etmek için oluşturulan her dosyada mümkün olan en az miktarda bilgi bulunmalıdır. Örneğin, bir haber makalesinin başlığını tutacak bir kompozisyon oluştururken haber makalesinin tamamı yerine yalnızca görüntülenmesi gereken bilgileri aktarmayı tercih edin:

@Composable
fun Header(title: String, subtitle: String) {
    // Recomposes when title or subtitle have changed.
}

@Composable
fun Header(news: News) {
    // Recomposes when a new instance of News is passed in.
}

Bazen bağımsız parametrelerin kullanılması performansı da artırır. Örneğin, News, title ve subtitle dışında daha fazla bilgi içeriyorsa title ve subtitle değişmemiş olsa bile yeni News örneği Header(news) içine geçirildiğinde, oluşturulan rapor yeniden oluşturulur.

İlettiğiniz parametre sayısını dikkatlice düşünün. Çok fazla parametreye sahip bir işlevin olması, işlevin ergonomikliğini azaltır. Bu nedenle, bu durumda söz konusu işlevin bir sınıf halinde gruplandırılması tercih edilir.

Compose'daki etkinlikler

Uygulamanıza yapılan her giriş bir etkinlik olarak gösterilmelidir (dokunmalar, metin değişiklikleri, hatta zamanlayıcılar ve diğer güncellemeler). Bu etkinlikler kullanıcı arayüzünüzün durumunu değiştirdiğinden, bunları işleyecek ve kullanıcı arayüzü durumunu güncelleyecek olan taraf ViewModel olmalıdır.

Kullanıcı arayüzü katmanının durumu hiçbir zaman etkinlik işleyici dışında değişmemelidir. Aksi takdirde uygulamanızda tutarsızlıklar ve hatalar oluşabilir.

Durum ve etkinlik işleyici lambda'ları için sabit değerlerin iletilmesini tercih edin. Bu yaklaşım aşağıdaki avantajları sağlar:

  • Yeniden kullanılabilirliği iyileştirmiş olursunuz.
  • Kullanıcı arayüzünüzün durumun değerini doğrudan değiştirmediğinden emin olmanız gerekir.
  • Durumun başka bir iş parçacığından dönüştürülmediğinden emin olduğunuz için eşzamanlılık sorunlarından kaçınırsınız.
  • Genellikle kod karmaşıklığını azaltırsınız.

Örneğin, parametre olarak bir String ve bir lambdayı kabul eden bir derleme, birçok bağlamdan çağrılabilir ve yüksek ölçüde yeniden kullanılabilir. Uygulamanızın üst tarafındaki uygulama çubuğunda her zaman metin gösterildiğini ve üzerinde geri düğmesi olduğunu varsayalım. Metni ve geri düğmesi tutma yerini parametre olarak alan daha genel bir MyAppTopAppBar oluşturabilirsiniz:

@Composable
fun MyAppTopAppBar(topAppBarText: String, onBackPressed: () -> Unit) {
    TopAppBar(
        title = {
            Text(
                text = topAppBarText,
                textAlign = TextAlign.Center,
                modifier = Modifier
                    .fillMaxSize()
                    .wrapContentSize(Alignment.Center)
            )
        },
        navigationIcon = {
            IconButton(onClick = onBackPressed) {
                Icon(
                    Icons.Filled.ArrowBack,
                    contentDescription = localizedString
                )
            }
        },
        // ...
    )
}

ViewModel'ler, durumlar ve etkinlikler: örnek

ViewModel ve mutableStateOf kullanarak aşağıdaki durumlardan birinin geçerli olması halinde uygulamanıza tek yönlü veri akışı da sağlayabilirsiniz:

  • Kullanıcı arayüzünüzün durumu, StateFlow veya LiveData gibi gözlemlenebilir durum sahipleri aracılığıyla gösterilir.
  • ViewModel, kullanıcı arayüzünden veya uygulamanızın diğer katmanlarından gelen etkinlikleri işler ve etkinliklere göre durum sahibini günceller.

Örneğin, bir oturum açma ekranını uygularken Oturum aç düğmesine dokunmak, uygulamanızda ilerleme göstergesi ve ağ çağrısının gösterilmesine neden olmalıdır. Giriş başarılı olursa uygulamanız farklı bir ekrana gider. Hata durumunda uygulama bir Snackbar gösterir. Ekran durumunu ve etkinliği şu şekilde modelleyebilirsiniz:

Ekranda dört durum vardır:

  • Oturum kapalıyken: Kullanıcı henüz oturum açmadığında.
  • Devam ediyor: Uygulamanız şu anda bir ağ çağrısı gerçekleştirerek kullanıcının oturumunu açmaya çalışıyordur.
  • Hata: Oturum açılırken bir hata oluştuğunda.
  • Oturum açık: Kullanıcı oturum açtığında.

Bu durumları kapalı sınıf olarak modelleyebilirsiniz. ViewModel, durumu State olarak gösterir, başlangıç durumunu ayarlar ve gerekirse durumu günceller. ViewModel, bir onSignIn() yöntemini göstererek oturum açma etkinliğini de işler.

class MyViewModel : ViewModel() {
    private val _uiState = mutableStateOf<UiState>(UiState.SignedOut)
    val uiState: State<UiState>
        get() = _uiState

    // ...
}

Compose, mutableStateOf API'nin yanı sıra LiveData, Flow ve Observable için işleyici olarak kaydolup değeri durum olarak temsil etmek üzere uzantılar sağlar.

class MyViewModel : ViewModel() {
    private val _uiState = MutableLiveData<UiState>(UiState.SignedOut)
    val uiState: LiveData<UiState>
        get() = _uiState

    // ...
}

@Composable
fun MyComposable(viewModel: MyViewModel) {
    val uiState = viewModel.uiState.observeAsState()
    // ...
}

Daha fazla bilgi

Jetpack Compose'daki mimari hakkında daha fazla bilgi edinmek için aşağıdaki kaynaklara bakın:

Sana Özel