CompositionLocal
, verileri kompozisyon üzerinden dolaylı olarak iletmek için kullanılan bir araçtır. Bu sayfada
CompositionLocal
öğesinin 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
ne kadar iyi karşıladığını
görebileceksiniz.
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ğıdaki örneğe bakın:
@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 kullanıcı arayüzü ağacının belirli bir düğümünde bir değerle sağlanır. Bu değer, composable alt öğeleri tarafından
CompositionLocal
, composable işlevde parametre olarak tanımlanıyor.
CompositionLocal
, Material temasının temelinde kullanılan öğedir.
MaterialTheme
, colorScheme
, typography
ve shapes
olmak üzere üç CompositionLocal
örneği sağlayan bir nesnedir. Bu örnekleri daha sonra bileşimin herhangi bir alt öğesinde alabilirsiniz.
Daha açık belirtmek gerekirse, MaterialTheme
colorScheme
, shapes
ve typography
özellikleri aracılığıyla erişebileceğiniz LocalColorScheme
, LocalShapes
ve LocalTypography
özellikleridir.
@Composable fun MyApp() { // Provides a Theme whose values are propagated down its `content` MaterialTheme { // New values for colorScheme, 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.colorScheme.primary ) }
CompositionLocal
örneği, kompozisyonun bir bölümüne göre kapsamlandırılır. Böylece ağacın farklı seviyelerinde farklı değerler sağlayabilirsiniz. current
değeri
bir CompositionLocal
,
üst öğe olabilir.
Bir CompositionLocal
için yeni bir değer sağlamak istiyorsanız CompositionLocalProvider
ve CompositionLocal
anahtarını value
ile ilişkilendiren provides
ara değer işlevini kullanın. CompositionLocalProvider
'un content
lambdası, CompositionLocal
'ın current
özelliğine erişirken sağlanan değeri alır. Bir
yeni değer sağlandığında, Compose, Beste'nin şu okunan kısımlarını yeniden
CompositionLocal
.
Buna bir örnek olarak, LocalContentColor
CompositionLocal
metin ve
kullanarak mevcut arka plan rengiyle kontrast oluşturun.
aşağıdaki örnekte CompositionLocalProvider
, farklı erişim düzeyleri sağlamak için
değerleri oluşturabilirsiniz.
@Composable fun CompositionLocalExample() { MaterialTheme { // Surface provides contentColorFor(MaterialTheme.colorScheme.surface) by default // This is to automatically make text and other content contrast to the background // correctly. Surface { Column { Text("Uses Surface's provided content color") CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.primary) { Text("Primary color provided by LocalContentColor") Text("This Text also uses primary as textColor") CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.error) { DescendantExample() } } } } } } @Composable fun DescendantExample() { // CompositionLocalProviders also work across composable functions Text("This Text uses the error color now") }
Şekil 1. CompositionLocalExample
composable'ın önizlemesi.
Son örnekte, CompositionLocal
örnekleri Material bileşenleri tarafından dahili olarak kullanılmıştır. Bir CompositionLocal
öğesinin mevcut değerine erişmek için current
özelliğini kullanın. Aşağıdaki örnekte, Android uygulamalarında yaygın olarak kullanılan LocalContext
CompositionLocal
öğesinin geçerli Context
değeri metni biçimlendirmek için kullanılır:
@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 izinleri için sorgu, temelde bir CompositionLocal
tarafından sağlanır. Bir medya seçici bileşeni, API'sini değiştirmeden ve medya seçiciyi çağıranların ortamda kullanılan bu ek bağlamdan haberdar olmasını gerektirmeden cihazdaki izin korumalı içeriğe erişmek için yeni işlevler ekleyebilir.
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.
Ayrıca, bu bağımlılık, kompozisyonun herhangi bir yerinde mutasyona uğrayabileceğinden, bu bağımlılık için net bir doğruluk kaynağı olmayabilir. Bu nedenle, current
değerinin nerede sağlandığını görmek için kompozisyonda yukarı doğru gitmeniz gerektiğinden sorun oluştuğunda uygulamada hata ayıklama işlemi daha zor olabilir. 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
'ü kullanım alanınız için iyi bir çözüm haline getirebilecek belirli koşullar vardır:
CompositionLocal
'in iyi bir varsayılan değeri olmalıdır. Varsayılan değer yoksa geliştiricilerin CompositionLocal
için bir değer sağlamadığı bir duruma düşmelerinin son derece zor olduğunu garanti etmeniz gerekir.
Varsayılan değer sağlamamak, test oluştururken veya bu özelliği kullanan bir bileşeni önizlerken sorunlara ve can sıkıcı durumlara neden olabilir. CompositionLocal
her zaman açıkça sağlanmalıdır.
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 koşulları karşılamıyorsa CompositionLocal
oluşturmadan önce Dikkate alınacak alternatifler bölümüne göz atın.
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,current
değerinin Kompozisyon'da okunduğu yerlerin yerineCompositionLocal
değerinin sağlandığıcontent
lambda'sının tamamının yeniden derlenmesine neden olur.
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. Uygulamanın farklı yükseklikleri kullanıcı arayüzü ağacı boyunca yayılacağından bir CompositionLocal
kullanırız. 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
bileşeni, değerleri belirli bir hiyerarşi için CompositionLocal
örneklerine bağlar. Bir CompositionLocal
için yeni bir değer sağlamak üzere, CompositionLocal
anahtarını value
ile ilişkilendiren provides
ara dize işlevini aşağıdaki gibi kullanın:
// 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
'ü tüketme
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 MyCard(elevation = LocalElevations.current.card) { // Content } }
Değerlendirebileceğiniz alternatifler
CompositionLocal
, bazı kullanım alanları için aşırı bir çözüm olabilir. Kullanım alanınız CompositionLocal'ı kullanıp kullanmayacağınıza karar verme bölümünde belirtilen ölçütleri karşılamıyorsa kullanım alanınıza daha uygun başka bir çözüm olabilir.
Belirli parametreleri iletme
Kompozit'in bağımlılıkları konusunda net olmak iyi bir alışkanlıktır. Kompozitlere yalnızca ihtiyaç duydukları bilgileri iletmenizi öneririz. Birleştirilebilir öğelerin ayrılmasını ve yeniden kullanılmasını teşvik etmek için her bir birleştirilebilir öğe mümkün olan en az miktarda bilgi içermelidir.
@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ün tersine çevrilmesi
Bir bileşene gereksiz bağımlılıkların aktarılmasını önlemenin bir diğer yolu da kontrolün tersine çevrilmesidir. Bir bağımlılığın yerine ana yayıncı bir mantık yürütür. Bunun yerine ana yayıncı yapar.
Bir alt öğenin bazı verileri yükleme isteğini tetiklemesi gereken aşağıdaki örneğe bakın:
@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
'ün çok fazla 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, alt öğeyi doğrudan üst öğelerinden ayırdığından bazı kullanım alanları için daha uygun olabilir. Üst öğe derlemeleri, daha esnek alt düzey derlemelere sahip olmak için daha karmaşık hale gelir.
Benzer şekilde, @Composable
içerik lambda'ları da aynı avantajlardan yararlanmak için aynı şekilde kullanılabilir:
@Composable fun MyComposable(myViewModel: MyViewModel = viewModel()) { // ... ReusablePartOfTheScreen( content = { Button( onClick = { myViewModel.loadData() } ) { Text("Confirm") } } ) } @Composable fun ReusablePartOfTheScreen(content: @Composable () -> Unit) { Column { // ... content() } }
Sizin için önerilenler
- Not: JavaScript kapalıyken bağlantı metni gösterilir
- Oluştur'daki bir temanın anatomisi
- Compose'da Görünümleri kullanma
- Jetpack Compose için Kotlin