[go: nahoru, domu]

blob: a4c716140adef29630c8cbd52576c46a01fe272d [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)? = null) {
@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 children of [CompositionLocalProvider] that read the value
* using [CompositionLocal.current].
*
* @param policy a policy to determine when a [CompositionLocal] is considered changed. See
* [SnapshotMutationPolicy] for details.
*
* @see CompositionLocal
* @see staticCompositionLocalOf
* @see mutableStateOf
*/
fun <T> compositionLocalOf(
policy: SnapshotMutationPolicy<T> =
@OptIn(ExperimentalComposeApi::class)
structuralEqualityPolicy(),
defaultFactory: (() -> T)? = null
): ProvidableCompositionLocal<T> = DynamicProvidableCompositionLocal(policy, defaultFactory)
/**
* Create a [CompositionLocal] key that can be provided using [CompositionLocalProvider]. Changing the value
* provided will cause the entire tree below [CompositionLocalProvider] to be recomposed, disabling skipping of
* composable calls.
*
* A static [CompositionLocal] should be only be used when the value provided is highly unlikely to
* change.
*
* @see CompositionLocal
* @see compositionLocalOf
*/
fun <T> staticCompositionLocalOf(defaultFactory: (() -> T)? = null): 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()
}