CompositionLocal

Class

Common
public sealed class CompositionLocal<T>(defaultFactory: () -> T)

Compose passes data through the composition tree explicitly through means of parameters to composable functions. This is often times the simplest and best way to have data flow through the tree.

Sometimes this model can be cumbersome or break down for data that is needed by lots of components, or when components need to pass data between one another but keep that implementation detail private. For these cases, CompositionLocals can be used as an implicit way to have data flow through a composition.

CompositionLocals by their nature are hierarchical. They make sense when the value of the CompositionLocal needs to be scoped to a particular sub-hierarchy of the composition.

One must create a CompositionLocal instance, which can be referenced by the consumers statically. CompositionLocal instances themselves hold no data, and can be thought of as a type-safe identifier for the data being passed down a tree. CompositionLocal factory functions take a single parameter: a factory to create a default value in cases where a CompositionLocal is used without a Provider. If this is a situation you would rather not handle, you can throw an error in this factory.

Somewhere up the tree, a CompositionLocalProvider component can be used, which provides a value for the CompositionLocal. This would often be at the "root" of a tree, but could be anywhere, and can also be used in multiple places to override the provided value for a sub-tree.

Intermediate components do not need to know about the CompositionLocal value, and can have zero dependencies on it. For example, SomeScreen might look like this:

Finally, a component that wishes to consume the CompositionLocal value can use the current property of the CompositionLocal key which returns the current value of the CompositionLocal, and subscribes the component to changes of it.

Code Examples

createCompositionLocal

fun createCompositionLocal() {
    val ActiveUser = compositionLocalOf<User> { error("No active user found!") }
}

compositionLocalProvider

fun compositionLocalProvider() {
    @Composable
    fun App(user: User) {
        CompositionLocalProvider(ActiveUser provides user) { SomeScreen() }
    }
}

someScreenSample

fun someScreenSample() {
    @Composable
    fun SomeScreen() {
        UserPhoto()
    }
}

consumeCompositionLocal

fun consumeCompositionLocal() {
    @Composable
    fun UserPhoto() {
        val user = ActiveUser.current
        ProfileIcon(src = user.profilePhotoUrl)
    }
}