I'm trying to understand how CompositionLocal
actually sets values implicitly and what requiremnts need to be met so that it works, but Android's documentation about Locally scoped data with CompositionLocal isn't helping.
There is an example where the color of a Text
is being changed by assigning a new value to the color
parameter.
// 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
)
}
But this is not implicit if you need to set it this way!
Then they show another example where they change the ContentAlpha
via the CompositionLocalProvider
and here the Text
suddenly can use the new value implicitly even though they write that CompositionLocal is what the Material theme uses under the hood. so why doesn't it work with the first example?
@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")
}
There's also a 3rd example where they show how to create your own CompositionLocal
, but here agian, they explicitly set the Card
's elevation
parameter!
@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
}
}
Didn't they just create the CompositionLocalProvider
to avoid doing this?
// 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
}
@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()
}
}
}
}
}
In this sample Text
sets alpha in source code using LocalContentAlpha.current
value with
val localContentColor = LocalContentColor.current
val localContentAlpha = LocalContentAlpha.current
val overrideColorOrUnspecified: Color = if (color.isSpecified) {
color
} else if (style.color.isSpecified) {
style.color
} else {
localContentColor.copy(localContentAlpha)
}
because of that changing value LocalContentAlpha
provides changes the alpha Text
gets.
As in third example where elevation is set with
Consuming the CompositionLocal CompositionLocal.current returns the value provided by the nearest CompositionLocalProvider that provides a value to that CompositionLocal:
@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
}
}
Also for instance with Icon which has default tint
tint: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
You can apply LocalContentAlpha or LocalContentColor to change default tint value or explicitly change param itself.
@Preview
@Composable
private fun Test() {
Column(
Modifier
.fillMaxSize()
.padding(10.dp)
) {
// Default icon
Icon(
imageVector = Icons.Default.Favorite,
contentDescription = null
)
Spacer(modifier = Modifier.height(10.dp))
Icon(
imageVector = Icons.Default.Favorite,
contentDescription = null,
tint = Color.Green
)
Spacer(modifier = Modifier.height(10.dp))
Icon(
modifier = Modifier.alpha(.3f),
imageVector = Icons.Default.Favorite,
tint = Color.Green,
contentDescription = null
)
Spacer(modifier = Modifier.height(10.dp))
// Icon uses tint: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
// changing it also changes default tint
CompositionLocalProvider(LocalContentColor provides Color.Green) {
Icon(
imageVector = Icons.Default.Favorite,
contentDescription = null
)
}
Spacer(modifier = Modifier.height(10.dp))
CompositionLocalProvider(LocalContentAlpha provides .3f) {
CompositionLocalProvider(LocalContentColor provides Color.Green) {
Icon(
imageVector = Icons.Default.Favorite,
contentDescription = null
)
}
}
}
}