CompositionLocal
, verileri Beste üzerinden örtülü olarak aktarmaya yarayan bir araçtır. Bu sayfada CompositionLocal
öğesinin ne olduğunu daha ayrıntılı bir şekilde, kendi CompositionLocal
öğenizi nasıl oluşturacağınızı ve CompositionLocal
kullanmanın kullanım alanınız için iyi bir çözüm olup olmadığını öğreneceksiniz.
CompositionLocal
ile tanışın
Compose'da genellikle her composable işleve parametre olarak kullanıcı arayüzü ağacı üzerinden veri akışı olur. Bu, bir composable'ın bağımlılıklarını açık hale getirir. Ancak bu, renkler veya tür stilleri gibi çok sık ve yaygın bir şekilde kullanılan veriler için külfetli olabilir. 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 ) }
Compose, CompositionLocal
ile renklerin çoğu composable'a açık parametre bağımlılığı olarak iletilmesine gerek kalmaz. Bu özellik, kullanıcı arayüzü ağacında veri akışını dolaylı yol olarak kullanabileceğiniz ağaç kapsamlı adlandırılmış nesneler oluşturmanıza olanak tanır.
CompositionLocal
öğelerine genellikle kullanıcı arayüzü ağacının belirli bir düğümünde bir değer sağlanır. Bu değer, CompositionLocal
öğesini composable işlevde parametre olarak bildirmeden composable alt öğeleri tarafından kullanılabilir.
Materyal tema, arka planda CompositionLocal
özelliğini kullanır.
MaterialTheme
üç CompositionLocal
örneği (renkler, tipografi ve şekiller) sağlayan bir nesnedir. Böylece bunları daha sonra Beste'nin herhangi bir alt bölümünde alabilirsiniz. Özellikle bunlar MaterialTheme
colors
, shapes
ve typography
özellikleri aracılığıyla erişebileceğiniz LocalColors
, LocalShapes
ve LocalTypography
özellikleridir.
@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 ) }
Bir CompositionLocal
örneği, Beste'nin bir bölümüne ayarlanır. Böylece ağacın farklı düzeylerinde farklı değerler sağlayabilirsiniz. CompositionLocal
öğesinin current
değeri, Bestenin ilgili bölümünde üst öğe tarafından sağlanan en yakın değere karşılık gelir.
Bir CompositionLocal
öğesine yeni bir değer sağlamak için CompositionLocalProvider
ve bir CompositionLocal
anahtarını value
ile ilişkilendiren provides
infix işlevini kullanın. CompositionLocalProvider
öğesinin content
lambda'sı, CompositionLocal
öğesinin current
özelliğine erişilirken sağlanan değeri alır. Yeni bir değer sağlandığında Compose, CompositionLocal
öğesini okuyan Beste bölümlerini yeniden oluşturur.
Buna örnek olarak, LocalContentAlpha
CompositionLocal
, kullanıcı arayüzünün farklı bölümlerini vurgulamak veya üzerindeki vurguyu azaltmak amacıyla metin ve ikonografi için kullanılan tercih edilen alfa içerik alfasını içerir. Aşağıdaki örnekte CompositionLocalProvider
, Bestenin farklı bölümlerine farklı değerler sağlamak için kullanılmıştır.
@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ın önizlemesi.
Yukarıdaki tüm örneklerde, CompositionLocal
örnekleri Material composable'ları tarafından dahili olarak kullanılmıştır. Bir CompositionLocal
öğesinin mevcut değerine erişmek için öğenin current
özelliğini kullanın. Aşağıdaki örnekte, metni biçimlendirmek için Android uygulamalarında yaygın olarak kullanılan LocalContext
CompositionLocal
öğesinin mevcut Context
değeri kullanılmıştı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
öğenizi oluşturma
CompositionLocal
, Beste'den dolaylı olarak veri aktarmak için kullanılan bir araçtır.
CompositionLocal
kullanımıyla ilgili bir diğer önemli sinyal, parametrenin kesişim gösterdiği zamandır ve bu ara katmanların bilinmesinin composable'ın faydasını sınırlayabileceğinden, ara uygulama katmanlarının bunun farkında olmaması gerekir. Örneğin, Android izinleri için sorgulama, arka planda bir CompositionLocal
tarafından sağlanır. composable bir medya seçme aracı, API'sini değiştirmeden ve medya seçiciyi arayanların ortamdan kullanılan bu ek bağlamın farkında olmasını gerektirmeden cihazda izne korunan içeriğe erişmek için yeni işlevler ekleyebilir.
Ancak, CompositionLocal
her zaman en iyi çözüm değildir. Bazı dezavantajlar getirmesi nedeniyle CompositionLocal
aşırı kullanılmasını önermiyoruz:
CompositionLocal
, bir composable'ın davranışının gerekçelendirilmesini zorlaştırır. Örtülü bağımlılıklar oluşturdukça, bunları kullanan composable'ları kullananların her CompositionLocal
için bir değerin karşılandığından emin olması gerekir.
Dahası, bu bağımlılığın Bestenin herhangi bir bölümünde değişebileceği için kesin bir doğruluk kaynağı olmayabilir. Bu nedenle, current
değerinin nerede sağlandığını görmek için Beste'de yukarı gitmeniz gerektiğinden, bir sorun oluştuğunda uygulamadaki hataları ayıklamak daha zor olabilir. IDE'deki Kullanımları bulma veya Compose düzen inceleyici gibi araçlar bu sorunu azaltmak için yeterli bilgi sağlar.
CompositionLocal
kullanılıp kullanılmayacağına karar verme
CompositionLocal
özelliğini kullanım alanınız için iyi bir çözüm olabilecek belirli koşullar vardır:
CompositionLocal
öğesinin varsayılan değeri iyi olmalıdır. Varsayılan değer yoksa bir geliştiricinin CompositionLocal
için bir değer sağlanmadığı bir durumla karşılaşmasının çok zor olacağını garanti etmeniz gerekir.
Varsayılan değerin sağlanmaması, test oluştururken veya CompositionLocal
öğesini kullanan bir composable'ı önizlerken sorun ve hayal kırıklığı yaratabilir. Bu durum, CompositionLocal
özelliğinin her zaman açıkça sağlanmasını gerektirir.
Ağaç kapsamlı veya alt hiyerarşi kapsamlı olarak düşünülmeyen kavramlar için CompositionLocal
kullanmaktan kaçının. CompositionLocal
, birkaç alt öğe tarafından değil, herhangi bir alt öğe tarafından kullanılabilecekse anlamlı olur.
Kullanım alanınız bu şartları karşılamıyorsa CompositionLocal
oluşturmadan önce Dikkate alınması gereken alternatifler bölümüne göz atın.
Belirli bir ekranın ViewModel
değerini tutan bir CompositionLocal
oluşturarak bu ekrandaki tüm composable'ların bir mantık gerçekleştirmek için ViewModel
öğesine referans vermesini sağlayabilirsiniz. Belirli bir kullanıcı arayüzü ağacının altındaki tüm composable'ların bir ViewModel
hakkında bilgi sahibi olması gerekmediğinden bu kötü bir uygulamadır. İyi bir uygulama, composable'lara yalnızca ihtiyaç duydukları bilgileri durumun aşağı inen ve etkinliklerin yukarı çıktığı düzene göre iletmektir. Bu yaklaşım composable'larınızı daha tekrar kullanılabilir
ve test edilmesini kolaylaştırır.
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ızcacurrent
değerini okuyan içeriği geçersiz kılar.staticCompositionLocalOf
:compositionLocalOf
'in aksine,staticCompositionLocalOf
okumaları Oluşturma tarafından izlenmez. Değerin değiştirilmesi, yalnızcacurrent
değerinin Bestede okunduğu yerler yerineCompositionLocal
öğesinin sağlandığıcontent
lambda'nın tamamının yeniden derlenmesine neden olur.
CompositionLocal
için sağlanan değerin değişme olasılığı çok düşükse veya hiçbir zaman değişmeyecekse performans avantajlarından yararlanmak için staticCompositionLocalOf
değerini kullanın.
Örneğin, bir uygulamanın tasarım sistemi, kullanıcı arayüzü bileşeninde bir gölge kullanılarak composable'ların yükseltildiği biçimde özenli bir şekilde düşünülebilir. Uygulama için farklı yüksekliklerin kullanıcı arayüzü ağacı boyunca yayılması gerektiğinden CompositionLocal
kullanırız. CompositionLocal
değeri, sistem temasına dayalı olarak koşullu olarak türetildiği için compositionLocalOf
API'yi kullanırız:
// 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
öğesine değerler sağlama
CompositionLocalProvider
composable, belirtilen hiyerarşi için değerleri CompositionLocal
örneğine bağlar. Bir CompositionLocal
öğesine yeni bir değer sağlamak için CompositionLocal
anahtarını bir value
ile aşağıdaki şekilde ilişkilendiren provides
infix işlevini 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üketimi
CompositionLocal.current
, söz konusu CompositionLocal
öğesine 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 } }
Göz önünde bulundurulması gereken alternatifler
CompositionLocal
, bazı kullanım alanları için aşırı bir çözüm olabilir. Kullanım alanınız, CompositionLocal özelliğinin kullanılıp kullanılmayacağına karar verme bölümünde belirtilen ölçütleri karşılamıyorsa kullanım alanınız için başka bir çözüm daha uygun olabilir.
Açık parametreleri iletme
composable'ın bağımlılıkları hakkında açık olmak iyi bir alışkanlıktır. Yalnızca ihtiyaç duydukları composable'ları iletmenizi öneririz. composable'ların ayrıştırılmasını ve yeniden kullanılmasını teşvik etmek için her composable'ın mümkün olan en az miktarda bilgi içermesi gerekir.
@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ü tersine çevirme
Bir composable'a gereksiz bağımlılıkları geçirmenin önüne geçmenin diğer bir yolu da kontrolü tersine çevirmektir. Alt öğenin bir mantık yürütmek için bağımlılık alması yerine bunu üst öğe yapar.
Bir alt öğenin, bazı verileri yüklemek için isteği tetiklemesi gereken aşağıdaki örneği inceleyin:
@Composable fun MyComposable(myViewModel: MyViewModel = viewModel()) { // ... MyDescendant(myViewModel) } @Composable fun MyDescendant(myViewModel: MyViewModel) { Button(onClick = { myViewModel.loadData() }) { Text("Load data") } }
Destek kaydına bağlı olarak, MyDescendant
şirketinin büyük sorumlulukları olabilir. Ayrıca, MyViewModel
öğesinin bağımlılık olarak geçirilmesi, MyDescendant
artık birbirlerine bağlı olduğundan daha az yeniden kullanılabilir olmasını sağlar. Bağımlılığı alt öğeye geçirmeyen ve üst öğeyi mantığı yürütmekten sorumlu yapan kontrol ilkelerinin ters çevrilmesini kullanan alternatifi düşünün:
@Composable fun MyComposable(myViewModel: MyViewModel = viewModel()) { // ... ReusableLoadDataButton( onLoadClick = { myViewModel.loadData() } ) } @Composable fun ReusableLoadDataButton(onLoadClick: () -> Unit) { Button(onClick = onLoadClick) { Text("Load data") } }
Bu yaklaşım, çocuğu kendi üstlerinden ayırdığı için bazı kullanım alanları için daha uygun olabilir. Üst composable'lar, daha esnek alt seviye composable'lara kıyasla daha karmaşık hale gelme eğilimindedir.
Benzer şekilde, @Composable
içerik lambda'sı aynı avantajları elde etmek 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: Bağlantı metni JavaScript kapalıyken görüntülenir
- Compose'da bir temanın anatomisi
- Oluşturma işleminde görünümleri kullanma
- Jetpack Compose için Kotlin