CompositionLocal ile yerel kapsamlı veriler

CompositionLocal, şunlar için bir araçtır: Dolaylı olarak Beste'den aşağıya doğru aktarılır. Bu sayfada CompositionLocal ne olduğunu daha ayrıntılı olarak, kendi şablonunuzu nasıl oluşturacağınızı öğrenin CompositionLocal ve CompositionLocal çözümünün aşağıdakiler için iyi bir çözüm olup olmadığını öğrenin en iyi uygulamaları görelim.

CompositionLocal ile tanışın

Compose'da genellikle veriler akıştan Her composable işlevin parametreleri olarak kullanıcı arayüzü ağacı. Bu, bir composable'ın açıklığa kavuşturmaktır. Ancak bu, çok büyük boyutlu ve yaygın olarak kullanılır. Aşağıdakilere göz atın örnek:

@Composable
fun MyApp() {
    // Theme information tends to be defined near the root of the application
    val colors = colors()
}

// Some composable deep in the hierarchy
@Composable
fun SomeTextLabel(labelText: String) {
    Text(
        text = labelText,
        color = colors.onPrimary // ← need to access colors here
    )
}

Renkleri Compose teklifleri CompositionLocal sayesinde kullanarak örtülü bir yöntem olarak kullanılabilecek ağaç kapsamlı adlandırılmış nesneler oluşturmak kullanıcı arayüzü ağacı üzerinden veri akışı sağlanıyor.

CompositionLocal öğeleri genellikle belirli bir düğümde bir değerle sağlanır örneğidir. Bu değer, composable alt öğeleri tarafından CompositionLocal, composable işlevde parametre olarak tanımlanıyor.

Materyal teması, arka planda CompositionLocal kullanır. MaterialTheme üç CompositionLocal örneği (renkler, yazı biçimi) sağlayan bir nesne Böylece, bu öğeleri daha sonra şablonun alt kısmındaki herhangi bir Beste. Bunlar LocalColors, LocalShapes ve MaterialTheme üzerinden erişebileceğiniz LocalTypography mülk colors, shapes ve typography özellikleri.

@Composable
fun MyApp() {
    // Provides a Theme whose values are propagated down its `content`
    MaterialTheme {
        // New values for colors, typography, and shapes are available
        // in MaterialTheme's content lambda.

        // ... content here ...
    }
}

// Some composable deep in the hierarchy of MaterialTheme
@Composable
fun SomeTextLabel(labelText: String) {
    Text(
        text = labelText,
        // `primary` is obtained from MaterialTheme's
        // LocalColors CompositionLocal
        color = MaterialTheme.colors.primary
    )
}

CompositionLocal örneği Beste'nin bir parçası olarak ayarlanır. Böylece, ağacın farklı düzeylerinde farklı değerler sağlayabilir. current değeri bir CompositionLocal, bestedeki bir üst öğe olarak kabul edilir.

CompositionLocal öğesine yeni bir değer sağlamak için CompositionLocalProvider ve onun provides CompositionLocal anahtarını value ile ilişkilendiren infix işlevi. İlgili içeriği oluşturmak için kullanılan Sağlanan CompositionLocalProvider, content lambda'yı alacak değeri için current ve CompositionLocal özelliğine erişirsiniz. Bir yeni değer sağlandığında, Compose, Beste'nin şu okunan kısımlarını yeniden CompositionLocal.

Buna bir örnek olarak, LocalContentAlpha CompositionLocal metin ve kullanıcı arayüzünün farklı bölümlerini vurgulamak veya vurgulamak için ikonlar. aşağıdaki örnekte CompositionLocalProvider, farklı erişim düzeyleri sağlamak için değerleri oluşturabilirsiniz.

@Composable
fun CompositionLocalExample() {
    MaterialTheme { // MaterialTheme sets ContentAlpha.high as default
        Column {
            Text("Uses MaterialTheme's provided alpha")
            CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
                Text("Medium value provided for LocalContentAlpha")
                Text("This Text also uses the medium value")
                CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) {
                    DescendantExample()
                }
            }
        }
    }
}

@Composable
fun DescendantExample() {
    // CompositionLocalProviders also work across composable functions
    Text("This Text uses the disabled alpha now")
}

Şekil 1. CompositionLocalExample composable'ın önizlemesi.

Yukarıdaki tüm örneklerde CompositionLocal örnekleri dahili olarak kullanılmıştır Material composable'dan oluşacak. Bir CompositionLocal öğesinin mevcut değerine erişmek için current kullanın Aşağıdaki örnekte LocalContext öğesinin mevcut Context değeri Android uygulamalarında yaygın olarak kullanılan CompositionLocal, biçimlendirmek için kullanılır metin:

@Composable
fun FruitText(fruitSize: Int) {
    // Get `resources` from the current value of LocalContext
    val resources = LocalContext.current.resources
    val fruitText = remember(resources, fruitSize) {
        resources.getQuantityString(R.plurals.fruit_title, fruitSize)
    }
    Text(text = fruitText)
}

Kendi CompositionLocal oluşturma

CompositionLocal, Beste’den aşağıya veri aktarmak için kullanılan bir araçtır dolaylı olarak oluşturabilirsiniz.

CompositionLocal kullanmak için bir diğer önemli sinyal, parametrenin kesişim ve ara uygulama katmanlarının farkında olmalısınız. vardır, çünkü bu ara katmanların farkında olmaları fayda sağlar. Örneğin, Android izinlerini sorgulamak için bu araçlar arasında yer alan bir CompositionLocal bulunur. Bir medya seçici composable izinle korunan içeriklere erişmek için API'sini değiştirmeden ve medya seçiciyi arayanların ortamdan kullanılan bu ek bağlamın farkında olun.

Ancak, CompositionLocal her zaman en iyi çözüm değildir. Biz Bazı dezavantajları beraberinde getirdiği için CompositionLocal'ın aşırı kullanımının önüne geçin:

CompositionLocal, bir composable'ın davranışının akıl yürütmesini zorlaştırır. Farklı örtülü bağımlılıklar oluşturuyorlar. Bunları kullanan composable’ları arayanlar her CompositionLocal için bir değere uygun olduğundan emin olun.

Dahası, bu bağımlılığın açık ve doğru bir kaynağı olmayabilir. Kompozisyonun herhangi bir bölümünde değişebilir. Dolayısıyla, bir istek olduğunda uygulamadaki daha zor olabilir, çünkü bu işlemi gerçekleştirmeniz current değerinin nerede sağlandığını görmek için beste. Find gibi araçlar kullanımları IDE veya Oluşturma düzen denetleyicisi'ndeki olabileceğini unutmayın.

CompositionLocal kullanılıp kullanılmayacağına karar veriliyor

CompositionLocal uygulamasını iyi bir çözüm haline getirebilecek belirli koşullar vardır bir örneği inceleyelim:

CompositionLocal iyi bir varsayılan değere sahip olmalıdır. Varsayılan ayar yoksa bir geliştirici için bunu yapmanın çok zor olduğunu CompositionLocal için bir değerin sağlanmadığı durumlarla karşılaşabilirsiniz. Varsayılan bir değer sağlanmaması, testlerde veya CompositionLocal öğesini kullanan bir composable'ın önizlemesi bu değerin açıkça sağlanmasını gerekli kılmaz.

Ağaç kapsamlı veya ağaç kapsamlı olarak düşünülmeyen kavramlar için CompositionLocal alt hiyerarşi kapsamındaki CompositionLocal, mümkün olduğunda anlam ifade eder birkaç alt öğe tarafından kullanıldığı düşünülebilir.

Kullanım alanınız bu şartları karşılamıyorsa Oluşturmadan önce Dikkate alınacak alternatifler bölümü CompositionLocal.

Kötü uygulamalara örnek olarak,CompositionLocal Belirli bir ekranın ViewModel kadarını ekleyebilirsiniz. Böylece o ekrandaki tüm composable'lar, bir mantık yürütmek için ViewModel öğesine referans alın. Bu kötü bir uygulama çünkü belirli bir kullanıcı arayüzü ağacının altındaki tüm composable'ların ViewModel İyi uygulama, yalnızca composable'lara bilgileri durumun aşağı, etkinliklerin yukarı akışı kalıbına uymaları gerekir. Bu yaklaşım, composable'larınızı daha verimli ve daha kolay test edilebilir.

CompositionLocal oluşturuluyor

CompositionLocal oluşturmak için iki API vardır:

  • compositionLocalOf: Yeniden oluşturma sırasında sağlanan değerin değiştirilmesi yalnızca geçersiz kılınır şu mesajı okuyan içerik: current değer.

  • staticCompositionLocalOf: compositionLocalOf işlevinin aksine, staticCompositionLocalOf okumaları Compose tarafından takip edilir. Değerin değiştirilmesi, content ayarının tamamen değişmesine neden olur. yerine CompositionLocal öğesinin yeniden oluşturulmasının sağlandığı lambda yalnızca current değerinin Beste'de okunduğu yerlerdir.

CompositionLocal için sağlanan değerin değişme olasılığı düşükse veya asla değişmeyecek. Performans avantajlarından yararlanmak için staticCompositionLocalOf kullanın.

Örneğin, bir uygulamanın tasarım sistemi, composable'ların değeri, kullanıcı arayüzü bileşeni için bir gölge kullanılarak yükseltilir. Farklı yükseltileri kullanıcı arayüzü ağacına yayılmalıdır. Bir CompositionLocal CompositionLocal değeri koşullu olarak türetildiğinden sistem temasına göre compositionLocalOf API'yi kullanıyoruz:

// LocalElevations.kt file

data class Elevations(val card: Dp = 0.dp, val default: Dp = 0.dp)

// Define a CompositionLocal global object with a default
// This instance can be accessed by all composables in the app
val LocalElevations = compositionLocalOf { Elevations() }

CompositionLocal için değer sağlama

CompositionLocalProvider composable, belirtilen değer için değerleri CompositionLocal örneğine bağlar hiyerarşi. Bir CompositionLocal için yeni bir değer sağlamak üzere provides CompositionLocal anahtarını aşağıdaki şekilde bir value ile ilişkilendiren infix işlevi:

// MyActivity.kt file

class MyActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            // Calculate elevations based on the system theme
            val elevations = if (isSystemInDarkTheme()) {
                Elevations(card = 1.dp, default = 1.dp)
            } else {
                Elevations(card = 0.dp, default = 0.dp)
            }

            // Bind elevation as the value for LocalElevations
            CompositionLocalProvider(LocalElevations provides elevations) {
                // ... Content goes here ...
                // This part of Composition will see the `elevations` instance
                // when accessing LocalElevations.current
            }
        }
    }
}

CompositionLocal kullanımı

CompositionLocal.current, söz konusu CompositionLocal için bir değer sağlayan en yakın CompositionLocalProvider tarafından sağlanan değeri döndürür:

@Composable
fun SomeComposable() {
    // Access the globally defined LocalElevations variable to get the
    // current Elevations in this part of the Composition
    Card(elevation = LocalElevations.current.card) {
        // Content
    }
}

Değerlendirilebilecek alternatifler

CompositionLocal, bazı kullanım alanları için aşırı bir çözüm olabilir. Eğer kullanım alanı kullanılıp kullanılmayacağına karar verme CompositionLocal bölümü gibi başka bir çözüm muhtemelen daha iyi olabilir bir şablon görevi görür.

Açık parametreleri iletme

composable'ın bağımlılıklarını açıkça belirtmek iyi bir alışkanlıktır. Önerilerimiz: composable'ları yalnızca ihtiyaç duydukları şeyleri iletin. Ayrıştırmayı teşvik etmek için ve yeniden kullanılması gereken composable'ların her biri, en az bilgi sağlar.

@Composable
fun MyComposable(myViewModel: MyViewModel = viewModel()) {
    // ...
    MyDescendant(myViewModel.data)
}

// Don't pass the whole object! Just what the descendant needs.
// Also, don't  pass the ViewModel as an implicit dependency using
// a CompositionLocal.
@Composable
fun MyDescendant(myViewModel: MyViewModel) { /* ... */ }

// Pass only what the descendant needs
@Composable
fun MyDescendant(data: DataToDisplay) {
    // Display data
}

Kontrolü ters çevirme

Gereksiz bağımlılıkları bir composable'a iletmekten kaçınmanın bir yolu da kontrolü ters çevirme yoluyla yapılır. Bir bağımlılığın yerine ana yayıncı belirli bir mantık yürütür.

Bir alt öğenin şu isteği tetiklemesi gereken aşağıdaki örneğe bakın: Bazı verileri yükleyin:

@Composable
fun MyComposable(myViewModel: MyViewModel = viewModel()) {
    // ...
    MyDescendant(myViewModel)
}

@Composable
fun MyDescendant(myViewModel: MyViewModel) {
    Button(onClick = { myViewModel.loadData() }) {
        Text("Load data")
    }
}

Duruma bağlı olarak MyDescendant adlı kuruluşun büyük sorumluluğu olabilir. Ayrıca, MyViewModel bağımlılığı nedeniyle MyDescendant daha az yeniden kullanılabilir çünkü Bunlar artık birbiriyle ilişkilendirildi. Şunlardan geçemeyen alternatifi düşünün: alt türe bağımlılığı artırır ve kontrol ilkelerinin tersini kullanır. üst öğeyi mantığı yürütmekten sorumlu hale getirir:

@Composable
fun MyComposable(myViewModel: MyViewModel = viewModel()) {
    // ...
    ReusableLoadDataButton(
        onLoadClick = {
            myViewModel.loadData()
        }
    )
}

@Composable
fun ReusableLoadDataButton(onLoadClick: () -> Unit) {
    Button(onClick = onLoadClick) {
        Text("Load data")
    }
}

Bu yaklaşım, farklı metodolojileri ve alt atalarından gelen çocuklardır. Üst composable'lar genellikle daha esnek alt düzey composable'lara olmaya başladı.

Benzer bir şekilde, @Composable içerik lambda'ları aynı avantajları sağlar:

@Composable
fun MyComposable(myViewModel: MyViewModel = viewModel()) {
    // ...
    ReusablePartOfTheScreen(
        content = {
            Button(
                onClick = {
                    myViewModel.loadData()
                }
            ) {
                Text("Confirm")
            }
        }
    )
}

@Composable
fun ReusablePartOfTheScreen(content: @Composable () -> Unit) {
    Column {
        // ...
        content()
    }
}

ziyaret edin.