blob: 5799751e2556667d5fb8da66bafa04ca07614608 [file] [log] [blame]
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.compose.runtime
/**
* 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, [CompositionLocal]s can be used as an implicit way to have data
* flow through a composition.
*
* [CompositionLocal]s 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.
*
* @sample androidx.compose.runtime.samples.createCompositionLocal
*
* 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.
*
* @sample androidx.compose.runtime.samples.compositionLocalProvider
*
* 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:
*
* @sample androidx.compose.runtime.samples.someScreenSample
*
* 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.
*
* @sample androidx.compose.runtime.samples.consumeCompositionLocal
*/
@Stable
sealed class CompositionLocal<T> constructor(defaultFactory: () -> T) {
@Suppress("UNCHECKED_CAST")
internal val defaultValueHolder = LazyValueHolder(defaultFactory)
@Composable
internal abstract fun provided(value: T): State<T>
/**
* Return the value provided by the nearest [CompositionLocalProvider] component that invokes, directly or
* indirectly, the composable function that uses this property.
*
* @sample androidx.compose.runtime.samples.consumeCompositionLocal
*/
@OptIn(InternalComposeApi::class)
inline val current: T
@ReadOnlyComposable
@Composable
get() = currentComposer.consume(this)
}
/**
* A [ProvidableCompositionLocal] can be used in [CompositionLocalProvider] to provide values.
*
* @see compositionLocalOf
* @see staticCompositionLocalOf
* @see CompositionLocal
* @see CompositionLocalProvider
*/
@Stable
abstract class ProvidableCompositionLocal<T> internal constructor(defaultFactory: () -> T) :
CompositionLocal<T> (defaultFactory) {
/**
* Associates a [CompositionLocal] key to a value in a call to [CompositionLocalProvider].
*
* @see CompositionLocal
* @see ProvidableCompositionLocal
*/
@Suppress("UNCHECKED_CAST")
infix fun provides(value: T) = ProvidedValue(this, value, true)
/**
* Associates a [CompositionLocal] key to a value in a call to [CompositionLocalProvider] if the key does not
* already have an associated value.
*
* @see CompositionLocal
* @see ProvidableCompositionLocal
*/
@Suppress("UNCHECKED_CAST")
infix fun providesDefault(value: T) = ProvidedValue(this, value, false)
}
/**
* A [DynamicProvidableCompositionLocal] is a [CompositionLocal] backed by [mutableStateOf].
* Providing new values using a [DynamicProvidableCompositionLocal] will provide the same [State]
* with a different value. Reading the [CompositionLocal] value of a
* [DynamicProvidableCompositionLocal] will record a read in the [RecomposeScope] of the
* composition. Changing the provided value will invalidate the [RecomposeScope]s.
*
* @see compositionLocalOf
*/
internal class DynamicProvidableCompositionLocal<T> constructor(
private val policy: SnapshotMutationPolicy<T>,
defaultFactory: () -> T
) : ProvidableCompositionLocal<T>(defaultFactory) {
@Composable
override fun provided(value: T): State<T> = remember { mutableStateOf(value, policy) }.apply {
this.value = value
}
}
/**
* A [StaticProvidableCompositionLocal] is a value that is expected to rarely change.
*
* @see staticCompositionLocalOf
*/
internal class StaticProvidableCompositionLocal<T>(defaultFactory: () -> T) :
ProvidableCompositionLocal<T>(defaultFactory) {
@Composable
override fun provided(value: T): State<T> = StaticValueHolder(value)
}
/**
* Create a [CompositionLocal] key that can be provided using [CompositionLocalProvider].
* Changing the value provided during recomposition will invalidate the content of
* [CompositionLocalProvider] that read the value using [CompositionLocal.current].
*
* [compositionLocalOf] creates a [ProvidableCompositionLocal] which can be used in a a call to
* [CompositionLocalProvider]. Similar to [MutableList] vs. [List], if the key is made public
* as [CompositionLocal] instead of [ProvidableCompositionLocal], it can be read using
* [CompositionLocal.current] but not re-provided.
*
* @param policy a policy to determine when a [CompositionLocal] is considered changed. See
* [SnapshotMutationPolicy] for details.
* @param defaultFactory a value factory to supply a value when a value is not provided. This
* factory is called when no value is provided through a [CompositionLocalProvider] of the caller
* of the component using [CompositionLocal.current]. If no reasonable default can be provided then
* consider throwing an exception.
*
* @see CompositionLocal
* @see staticCompositionLocalOf
* @see mutableStateOf
*/
fun <T> compositionLocalOf(
policy: SnapshotMutationPolicy<T> =
structuralEqualityPolicy(),
defaultFactory: () -> T
): ProvidableCompositionLocal<T> = DynamicProvidableCompositionLocal(policy, defaultFactory)
/**
* Create a [CompositionLocal] key that can be provided using [CompositionLocalProvider].
*
* Unlike [compositionLocalOf], reads of a [staticCompositionLocalOf] are not tracked by the
* composer and changing the value provided in the [CompositionLocalProvider] call will cause the
* entirety of the content to be recomposed instead of just the places where in the composition the
* local value is used. This lack of tracking, however, makes a [staticCompositionLocalOf] more
* efficient when the value provided is highly unlikely to or will never change. For example,
* the android context, font loaders, or similar shared values, are unlikely to change for the
* components in the content of a the [CompositionLocalProvider] and should consider using a
* [staticCompositionLocalOf]. A color, or other theme like value, might change or even be
* animated therefore a [compositionLocalOf] should be used.
*
* [staticCompositionLocalOf] creates a [ProvidableCompositionLocal] which can be used in a a
* call to [CompositionLocalProvider]. Similar to [MutableList] vs. [List], if the key is made
* public as [CompositionLocal] instead of [ProvidableCompositionLocal], it can be read using
* [CompositionLocal.current] but not re-provided.
*
* @param defaultFactory a value factory to supply a value when a value is not provided. This
* factory is called when no value is provided through a [CompositionLocalProvider] of the caller
* of the component using [CompositionLocal.current]. If no reasonable default can be provided then
* consider throwing an exception.
*
* @see CompositionLocal
* @see compositionLocalOf
*/
fun <T> staticCompositionLocalOf(defaultFactory: () -> T): ProvidableCompositionLocal<T> =
StaticProvidableCompositionLocal(defaultFactory)
/**
* [CompositionLocalProvider] binds values to [ProvidableCompositionLocal] keys. Reading the
* [CompositionLocal] using [CompositionLocal.current] will return the value provided in
* [CompositionLocalProvider]'s [values] parameter for all composable functions called directly
* or indirectly in the [content] lambda.
*
* @sample androidx.compose.runtime.samples.compositionLocalProvider
*
* @see CompositionLocal
* @see compositionLocalOf
* @see staticCompositionLocalOf
*/
@Composable
@OptIn(InternalComposeApi::class)
fun CompositionLocalProvider(vararg values: ProvidedValue<*>, content: @Composable () -> Unit) {
currentComposer.startProviders(values)
content()
currentComposer.endProviders()
}