1 /*
2 * Copyright 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package androidx.compose.runtime
18
19 /**
20 * Compose passes data through the composition tree explicitly through means of parameters to
21 * composable functions. This is often times the simplest and best way to have data flow through the
22 * tree.
23 *
24 * Sometimes this model can be cumbersome or break down for data that is needed by lots of
25 * components, or when components need to pass data between one another but keep that implementation
26 * detail private. For these cases, [CompositionLocal]s can be used as an implicit way to have data
27 * flow through a composition.
28 *
29 * [CompositionLocal]s by their nature are hierarchical. They make sense when the value of the
30 * [CompositionLocal] needs to be scoped to a particular sub-hierarchy of the composition.
31 *
32 * One must create a [CompositionLocal] instance, which can be referenced by the consumers
33 * statically. [CompositionLocal] instances themselves hold no data, and can be thought of as a
34 * type-safe identifier for the data being passed down a tree. [CompositionLocal] factory functions
35 * take a single parameter: a factory to create a default value in cases where a [CompositionLocal]
36 * is used without a Provider. If this is a situation you would rather not handle, you can throw an
37 * error in this factory.
38 *
39 * @sample androidx.compose.runtime.samples.createCompositionLocal
40 *
41 * Somewhere up the tree, a [CompositionLocalProvider] component can be used, which provides a value
42 * for the [CompositionLocal]. This would often be at the "root" of a tree, but could be anywhere,
43 * and can also be used in multiple places to override the provided value for a sub-tree.
44 *
45 * @sample androidx.compose.runtime.samples.compositionLocalProvider
46 *
47 * Intermediate components do not need to know about the [CompositionLocal] value, and can have zero
48 * dependencies on it. For example, `SomeScreen` might look like this:
49 *
50 * @sample androidx.compose.runtime.samples.someScreenSample
51 *
52 * Finally, a component that wishes to consume the [CompositionLocal] value can use the [current]
53 * property of the [CompositionLocal] key which returns the current value of the [CompositionLocal],
54 * and subscribes the component to changes of it.
55 *
56 * @sample androidx.compose.runtime.samples.consumeCompositionLocal
57 */
58 @Stable
59 sealed class CompositionLocal<T>(defaultFactory: () -> T) {
60 internal open val defaultValueHolder: ValueHolder<T> = LazyValueHolder(defaultFactory)
61
updatedStateOfnull62 internal abstract fun updatedStateOf(
63 value: ProvidedValue<T>,
64 previous: ValueHolder<T>?
65 ): ValueHolder<T>
66
67 /**
68 * Return the value provided by the nearest [CompositionLocalProvider] component that invokes,
69 * directly or indirectly, the composable function that uses this property.
70 *
71 * @sample androidx.compose.runtime.samples.consumeCompositionLocal
72 */
73 @OptIn(InternalComposeApi::class)
74 inline val current: T
75 @ReadOnlyComposable @Composable get() = currentComposer.consume(this)
76 }
77
78 /**
79 * A [ProvidableCompositionLocal] can be used in [CompositionLocalProvider] to provide values.
80 *
81 * @see compositionLocalOf
82 * @see staticCompositionLocalOf
83 * @see CompositionLocal
84 * @see CompositionLocalProvider
85 */
86 @Stable
87 abstract class ProvidableCompositionLocal<T> internal constructor(defaultFactory: () -> T) :
88 CompositionLocal<T>(defaultFactory) {
89 internal abstract fun defaultProvidedValue(value: T): ProvidedValue<T>
90
91 /**
92 * Associates a [CompositionLocal] key to a value in a call to [CompositionLocalProvider].
93 *
94 * @see CompositionLocal
95 * @see ProvidableCompositionLocal
96 */
97 infix fun provides(value: T) = defaultProvidedValue(value)
98
99 /**
100 * Associates a [CompositionLocal] key to a value in a call to [CompositionLocalProvider] if the
101 * key does not already have an associated value.
102 *
103 * @see CompositionLocal
104 * @see ProvidableCompositionLocal
105 */
106 infix fun providesDefault(value: T) = defaultProvidedValue(value).ifNotAlreadyProvided()
107
108 /**
109 * Associates a [CompositionLocal] key to a lambda, [compute], in a call to [CompositionLocal].
110 * The [compute] lambda is invoked whenever the key is retrieved. The lambda is executed in the
111 * context of a [CompositionLocalContext] which allow retrieving the current values of other
112 * composition locals by calling [CompositionLocalAccessorScope.currentValue], which is an
113 * extension function provided by the context for a [CompositionLocal] key.
114 *
115 * The lambda passed to [providesComputed] will be invoked every time the
116 * [CompositionLocal.current] is evaluated for the composition local and computes its value
117 * based on the current value of the locals referenced in the lambda at the time
118 * [CompositionLocal.current] is evaluated. This allows providing values that can be derived
119 * from other locals. For example, if accent colors can be calculated from a single base color,
120 * the accent colors can be provided as computed composition locals. Providing a new base color
121 * would automatically update all the accent colors.
122 *
123 * @sample androidx.compose.runtime.samples.compositionLocalProvidedComputed
124 * @sample androidx.compose.runtime.samples.compositionLocalComputedAfterProvidingLocal
125 * @see CompositionLocal
126 * @see CompositionLocalContext
127 * @see ProvidableCompositionLocal
128 */
129 infix fun providesComputed(compute: CompositionLocalAccessorScope.() -> T) =
130 ProvidedValue(
131 compositionLocal = this,
132 value = null,
133 explicitNull = false,
134 mutationPolicy = null,
135 state = null,
136 compute = compute,
137 isDynamic = false
138 )
139
140 override fun updatedStateOf(
141 value: ProvidedValue<T>,
142 previous: ValueHolder<T>?
143 ): ValueHolder<T> {
144 return when (previous) {
145 is DynamicValueHolder ->
146 if (value.isDynamic) {
147 previous.state.value = value.effectiveValue
148 previous
149 } else null
150 is StaticValueHolder ->
151 if (value.isStatic && value.effectiveValue == previous.value) previous else null
152 is ComputedValueHolder -> if (value.compute === previous.compute) previous else null
153 else -> null
154 } ?: valueHolderOf(value)
155 }
156
157 private fun valueHolderOf(value: ProvidedValue<T>): ValueHolder<T> =
158 when {
159 value.isDynamic ->
160 DynamicValueHolder(
161 value.state
162 ?: mutableStateOf(
163 value.value,
164 value.mutationPolicy ?: structuralEqualityPolicy()
165 )
166 )
167 value.compute != null -> ComputedValueHolder(value.compute)
168 value.state != null -> DynamicValueHolder(value.state)
169 else -> StaticValueHolder(value.effectiveValue)
170 }
171 }
172
173 /**
174 * A [DynamicProvidableCompositionLocal] is a [CompositionLocal] backed by [mutableStateOf].
175 * Providing new values using a [DynamicProvidableCompositionLocal] will provide the same [State]
176 * with a different value. Reading the [CompositionLocal] value of a
177 * [DynamicProvidableCompositionLocal] will record a read in the [RecomposeScope] of the
178 * composition. Changing the provided value will invalidate the [RecomposeScope]s.
179 *
180 * @see compositionLocalOf
181 */
182 internal class DynamicProvidableCompositionLocal<T>(
183 private val policy: SnapshotMutationPolicy<T>,
184 defaultFactory: () -> T
185 ) : ProvidableCompositionLocal<T>(defaultFactory) {
186
defaultProvidedValuenull187 override fun defaultProvidedValue(value: T) =
188 ProvidedValue(
189 compositionLocal = this,
190 value = value,
191 explicitNull = value === null,
192 mutationPolicy = policy,
193 state = null,
194 compute = null,
195 isDynamic = true
196 )
197 }
198
199 /**
200 * A [StaticProvidableCompositionLocal] is a value that is expected to rarely change.
201 *
202 * @see staticCompositionLocalOf
203 */
204 internal class StaticProvidableCompositionLocal<T>(defaultFactory: () -> T) :
205 ProvidableCompositionLocal<T>(defaultFactory) {
206
207 override fun defaultProvidedValue(value: T) =
208 ProvidedValue(
209 compositionLocal = this,
210 value = value,
211 explicitNull = value === null,
212 mutationPolicy = null,
213 state = null,
214 compute = null,
215 isDynamic = false
216 )
217 }
218
219 /**
220 * Create a [CompositionLocal] key that can be provided using [CompositionLocalProvider]. Changing
221 * the value provided during recomposition will invalidate the content of [CompositionLocalProvider]
222 * that read the value using [CompositionLocal.current].
223 *
224 * [compositionLocalOf] creates a [ProvidableCompositionLocal] which can be used in a a call to
225 * [CompositionLocalProvider]. Similar to [MutableList] vs. [List], if the key is made public as
226 * [CompositionLocal] instead of [ProvidableCompositionLocal], it can be read using
227 * [CompositionLocal.current] but not re-provided.
228 *
229 * @param policy a policy to determine when a [CompositionLocal] is considered changed. See
230 * [SnapshotMutationPolicy] for details.
231 * @param defaultFactory a value factory to supply a value when a value is not provided. This
232 * factory is called when no value is provided through a [CompositionLocalProvider] of the caller
233 * of the component using [CompositionLocal.current]. If no reasonable default can be provided
234 * then consider throwing an exception.
235 * @see CompositionLocal
236 * @see staticCompositionLocalOf
237 * @see mutableStateOf
238 */
compositionLocalOfnull239 fun <T> compositionLocalOf(
240 policy: SnapshotMutationPolicy<T> = structuralEqualityPolicy(),
241 defaultFactory: () -> T
242 ): ProvidableCompositionLocal<T> = DynamicProvidableCompositionLocal(policy, defaultFactory)
243
244 /**
245 * Create a [CompositionLocal] key that can be provided using [CompositionLocalProvider].
246 *
247 * Unlike [compositionLocalOf], reads of a [staticCompositionLocalOf] are not tracked by the
248 * composer and changing the value provided in the [CompositionLocalProvider] call will cause the
249 * entirety of the content to be recomposed instead of just the places where in the composition the
250 * local value is used. This lack of tracking, however, makes a [staticCompositionLocalOf] more
251 * efficient when the value provided is highly unlikely to or will never change. For example, the
252 * android context, font loaders, or similar shared values, are unlikely to change for the
253 * components in the content of a the [CompositionLocalProvider] and should consider using a
254 * [staticCompositionLocalOf]. A color, or other theme like value, might change or even be animated
255 * therefore a [compositionLocalOf] should be used.
256 *
257 * [staticCompositionLocalOf] creates a [ProvidableCompositionLocal] which can be used in a a call
258 * to [CompositionLocalProvider]. Similar to [MutableList] vs. [List], if the key is made public as
259 * [CompositionLocal] instead of [ProvidableCompositionLocal], it can be read using
260 * [CompositionLocal.current] but not re-provided.
261 *
262 * @param defaultFactory a value factory to supply a value when a value is not provided. This
263 * factory is called when no value is provided through a [CompositionLocalProvider] of the caller
264 * of the component using [CompositionLocal.current]. If no reasonable default can be provided
265 * then consider throwing an exception.
266 * @see CompositionLocal
267 * @see compositionLocalOf
268 */
269 fun <T> staticCompositionLocalOf(defaultFactory: () -> T): ProvidableCompositionLocal<T> =
270 StaticProvidableCompositionLocal(defaultFactory)
271
272 /**
273 * Create a [CompositionLocal] that behaves like it was provided using
274 * [ProvidableCompositionLocal.providesComputed] by default. If a value is provided using
275 * [ProvidableCompositionLocal.provides] it behaves as if the [CompositionLocal] was produced by
276 * calling [compositionLocalOf].
277 *
278 * In other words, a [CompositionLocal] produced by can be provided identically to
279 * [CompositionLocal] created with [compositionLocalOf] with the only difference is how it behaves
280 * when the value is not provided. For a [compositionLocalOf] the default value is returned. If no
281 * default value has be computed for [CompositionLocal] the default computation is called.
282 *
283 * The lambda passed to [compositionLocalWithComputedDefaultOf] will be invoked every time the
284 * [CompositionLocal.current] is evaluated for the composition local and computes its value based on
285 * the current value of the locals referenced in the lambda at the time [CompositionLocal.current]
286 * is evaluated. This allows providing values that can be derived from other locals. For example, if
287 * accent colors can be calculated from a single base color, the accent colors can be provided as
288 * computed composition locals. Providing a new base color would automatically update all the accent
289 * colors.
290 *
291 * @sample androidx.compose.runtime.samples.compositionLocalComputedByDefault
292 * @sample androidx.compose.runtime.samples.compositionLocalComputedAfterProvidingLocal
293 * @param defaultComputation the default computation to use when this [CompositionLocal] is not
294 * provided.
295 * @see CompositionLocal
296 * @see ProvidableCompositionLocal
297 */
298 fun <T> compositionLocalWithComputedDefaultOf(
299 defaultComputation: CompositionLocalAccessorScope.() -> T
300 ): ProvidableCompositionLocal<T> = ComputedProvidableCompositionLocal(defaultComputation)
301
302 internal class ComputedProvidableCompositionLocal<T>(
303 defaultComputation: CompositionLocalAccessorScope.() -> T
304 ) : ProvidableCompositionLocal<T>({ composeRuntimeError("Unexpected call to default provider") }) {
305 override val defaultValueHolder = ComputedValueHolder(defaultComputation)
306
defaultProvidedValuenull307 override fun defaultProvidedValue(value: T): ProvidedValue<T> =
308 ProvidedValue(
309 compositionLocal = this,
310 value = value,
311 explicitNull = value === null,
312 mutationPolicy = null,
313 state = null,
314 compute = null,
315 isDynamic = true
316 )
317 }
318
319 interface CompositionLocalAccessorScope {
320 /**
321 * An extension property that allows accessing the current value of a composition local in the
322 * context of this scope. This scope is the type of the `this` parameter when in a computed
323 * composition. Computed composition locals can be provided by either using
324 * [compositionLocalWithComputedDefaultOf] or by using the
325 * [ProvidableCompositionLocal.providesComputed] infix operator.
326 *
327 * @sample androidx.compose.runtime.samples.compositionLocalProvidedComputed
328 * @see ProvidableCompositionLocal
329 * @see ProvidableCompositionLocal.providesComputed
330 * @see ProvidableCompositionLocal.provides
331 * @see CompositionLocalProvider
332 */
333 val <T> CompositionLocal<T>.currentValue: T
334 }
335
336 /**
337 * Stores [CompositionLocal]'s and their values.
338 *
339 * Can be obtained via [currentCompositionLocalContext] and passed to another composition via
340 * [CompositionLocalProvider].
341 *
342 * [CompositionLocalContext] is immutable and won't be changed after its obtaining.
343 */
344 @Stable
345 class CompositionLocalContext
346 internal constructor(internal val compositionLocals: PersistentCompositionLocalMap)
347
348 /**
349 * [CompositionLocalProvider] binds values to [ProvidableCompositionLocal] keys. Reading the
350 * [CompositionLocal] using [CompositionLocal.current] will return the value provided in
351 * [CompositionLocalProvider]'s [values] parameter for all composable functions called directly or
352 * indirectly in the [content] lambda.
353 *
354 * @sample androidx.compose.runtime.samples.compositionLocalProvider
355 * @see CompositionLocal
356 * @see compositionLocalOf
357 * @see staticCompositionLocalOf
358 */
359 @Composable
360 @OptIn(InternalComposeApi::class)
361 @NonSkippableComposable
CompositionLocalProvidernull362 fun CompositionLocalProvider(vararg values: ProvidedValue<*>, content: @Composable () -> Unit) {
363 currentComposer.startProviders(values)
364 content()
365 currentComposer.endProviders()
366 }
367
368 /**
369 * [CompositionLocalProvider] binds value to [ProvidableCompositionLocal] key. Reading the
370 * [CompositionLocal] using [CompositionLocal.current] will return the value provided in
371 * [CompositionLocalProvider]'s [value] parameter for all composable functions called directly or
372 * indirectly in the [content] lambda.
373 *
374 * @sample androidx.compose.runtime.samples.compositionLocalProvider
375 * @see CompositionLocal
376 * @see compositionLocalOf
377 * @see staticCompositionLocalOf
378 */
379 @Composable
380 @OptIn(InternalComposeApi::class)
381 @NonSkippableComposable
CompositionLocalProvidernull382 fun CompositionLocalProvider(value: ProvidedValue<*>, content: @Composable () -> Unit) {
383 currentComposer.startProvider(value)
384 content()
385 currentComposer.endProvider()
386 }
387
388 /**
389 * [CompositionLocalProvider] binds values to [CompositionLocal]'s, provided by [context]. Reading
390 * the [CompositionLocal] using [CompositionLocal.current] will return the value provided in values
391 * stored inside [context] for all composable functions called directly or indirectly in the
392 * [content] lambda.
393 *
394 * @sample androidx.compose.runtime.samples.compositionLocalProvider
395 * @see CompositionLocal
396 * @see compositionLocalOf
397 * @see staticCompositionLocalOf
398 */
399 @Composable
CompositionLocalProvidernull400 fun CompositionLocalProvider(context: CompositionLocalContext, content: @Composable () -> Unit) {
401 CompositionLocalProvider(
402 *context.compositionLocals.map { it.value.toProvided(it.key) }.toTypedArray(),
403 content = content
404 )
405 }
406