1 /*
<lambda>null2 * 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 @file:JvmName("SnapshotStateKt")
18 @file:JvmMultifileClass
19
20 package androidx.compose.runtime
21
22 import androidx.compose.runtime.snapshots.GlobalSnapshot
23 import androidx.compose.runtime.snapshots.Snapshot
24 import androidx.compose.runtime.snapshots.SnapshotId
25 import androidx.compose.runtime.snapshots.SnapshotMutableState
26 import androidx.compose.runtime.snapshots.SnapshotStateList
27 import androidx.compose.runtime.snapshots.SnapshotStateMap
28 import androidx.compose.runtime.snapshots.SnapshotStateSet
29 import androidx.compose.runtime.snapshots.StateFactoryMarker
30 import androidx.compose.runtime.snapshots.StateObjectImpl
31 import androidx.compose.runtime.snapshots.StateRecord
32 import androidx.compose.runtime.snapshots.currentSnapshot
33 import androidx.compose.runtime.snapshots.overwritable
34 import androidx.compose.runtime.snapshots.readable
35 import androidx.compose.runtime.snapshots.toSnapshotId
36 import androidx.compose.runtime.snapshots.withCurrent
37 import kotlin.jvm.JvmMultifileClass
38 import kotlin.jvm.JvmName
39 import kotlin.reflect.KProperty
40
41 /**
42 * Return a new [MutableState] initialized with the passed in [value]
43 *
44 * The MutableState class is a single value holder whose reads and writes are observed by Compose.
45 * Additionally, writes to it are transacted as part of the [Snapshot] system.
46 *
47 * @param value the initial value for the [MutableState]
48 * @param policy a policy to controls how changes are handled in mutable snapshots.
49 * @sample androidx.compose.runtime.samples.SimpleStateSample
50 * @sample androidx.compose.runtime.samples.DestructuredStateSample
51 * @sample androidx.compose.runtime.samples.observeUserSample
52 * @sample androidx.compose.runtime.samples.stateSample
53 * @see State
54 * @see MutableState
55 * @see SnapshotMutationPolicy
56 * @see mutableIntStateOf
57 * @see mutableLongStateOf
58 * @see mutableFloatStateOf
59 * @see mutableDoubleStateOf
60 */
61 @StateFactoryMarker
62 fun <T> mutableStateOf(
63 value: T,
64 policy: SnapshotMutationPolicy<T> = structuralEqualityPolicy()
65 ): MutableState<T> = createSnapshotMutableState(value, policy)
66
67 /**
68 * A value holder where reads to the [value] property during the execution of a [Composable]
69 * function, the current [RecomposeScope] will be subscribed to changes of that value.
70 *
71 * @see [MutableState]
72 * @see [mutableStateOf]
73 */
74 @Stable
75 interface State<out T> {
76 val value: T
77 }
78
79 /**
80 * Permits property delegation of `val`s using `by` for [State].
81 *
82 * @sample androidx.compose.runtime.samples.DelegatedReadOnlyStateSample
83 */
84 @Suppress("NOTHING_TO_INLINE")
getValuenull85 inline operator fun <T> State<T>.getValue(thisObj: Any?, property: KProperty<*>): T = value
86
87 /**
88 * A mutable value holder where reads to the [value] property during the execution of a [Composable]
89 * function, the current [RecomposeScope] will be subscribed to changes of that value. When the
90 * [value] property is written to and changed, a recomposition of any subscribed [RecomposeScope]s
91 * will be scheduled. If [value] is written to with the same value, no recompositions will be
92 * scheduled.
93 *
94 * @see [State]
95 * @see [mutableStateOf]
96 */
97 @Stable
98 interface MutableState<T> : State<T> {
99 override var value: T
100
101 operator fun component1(): T
102
103 operator fun component2(): (T) -> Unit
104 }
105
106 /**
107 * Permits property delegation of `var`s using `by` for [MutableState].
108 *
109 * @sample androidx.compose.runtime.samples.DelegatedStateSample
110 */
111 @Suppress("NOTHING_TO_INLINE")
setValuenull112 inline operator fun <T> MutableState<T>.setValue(thisObj: Any?, property: KProperty<*>, value: T) {
113 this.value = value
114 }
115
116 /** Returns platform specific implementation based on [SnapshotMutableStateImpl]. */
createSnapshotMutableStatenull117 internal expect fun <T> createSnapshotMutableState(
118 value: T,
119 policy: SnapshotMutationPolicy<T>
120 ): SnapshotMutableState<T>
121
122 /**
123 * A single value holder whose reads and writes are observed by Compose.
124 *
125 * Additionally, writes to it are transacted as part of the [Snapshot] system.
126 *
127 * @param value the wrapped value
128 * @param policy a policy to control how changes are handled in a mutable snapshot.
129 * @see mutableStateOf
130 * @see SnapshotMutationPolicy
131 */
132 internal open class SnapshotMutableStateImpl<T>(
133 value: T,
134 override val policy: SnapshotMutationPolicy<T>
135 ) : StateObjectImpl(), SnapshotMutableState<T> {
136 @Suppress("UNCHECKED_CAST")
137 override var value: T
138 get() = next.readable(this).value
139 set(value) =
140 next.withCurrent {
141 if (!policy.equivalent(it.value, value)) {
142 next.overwritable(this, it) { this.value = value }
143 }
144 }
145
146 private var next: StateStateRecord<T> =
147 currentSnapshot().let { snapshot ->
148 StateStateRecord(snapshot.snapshotId, value).also {
149 if (snapshot !is GlobalSnapshot) {
150 it.next = StateStateRecord(Snapshot.PreexistingSnapshotId.toSnapshotId(), value)
151 }
152 }
153 }
154
155 override val firstStateRecord: StateRecord
156 get() = next
157
158 override fun prependStateRecord(value: StateRecord) {
159 @Suppress("UNCHECKED_CAST")
160 next = value as StateStateRecord<T>
161 }
162
163 @Suppress("UNCHECKED_CAST")
164 override fun mergeRecords(
165 previous: StateRecord,
166 current: StateRecord,
167 applied: StateRecord
168 ): StateRecord? {
169 val previousRecord = previous as StateStateRecord<T>
170 val currentRecord = current as StateStateRecord<T>
171 val appliedRecord = applied as StateStateRecord<T>
172 return if (policy.equivalent(currentRecord.value, appliedRecord.value)) current
173 else {
174 val merged =
175 policy.merge(previousRecord.value, currentRecord.value, appliedRecord.value)
176 if (merged != null) {
177 appliedRecord.create(appliedRecord.snapshotId).also { it.value = merged }
178 } else {
179 null
180 }
181 }
182 }
183
184 override fun toString(): String =
185 next.withCurrent { "MutableState(value=${it.value})@${hashCode()}" }
186
187 private class StateStateRecord<T>(snapshotId: SnapshotId, myValue: T) :
188 StateRecord(snapshotId) {
189 override fun assign(value: StateRecord) {
190 @Suppress("UNCHECKED_CAST")
191 this.value = (value as StateStateRecord<T>).value
192 }
193
194 override fun create() = StateStateRecord(currentSnapshot().snapshotId, value)
195
196 override fun create(snapshotId: SnapshotId) =
197 StateStateRecord(currentSnapshot().snapshotId, value)
198
199 var value: T = myValue
200 }
201
202 /**
203 * The componentN() operators allow state objects to be used with the property destructuring
204 * syntax
205 *
206 * ```
207 * var (foo, setFoo) = remember { mutableStateOf(0) }
208 * setFoo(123) // set
209 * foo == 123 // get
210 * ```
211 */
212 override operator fun component1(): T = value
213
214 override operator fun component2(): (T) -> Unit = { value = it }
215
216 /**
217 * A function used by the debugger to display the value of the current value of the mutable
218 * state object without triggering read observers.
219 */
220 @Suppress("unused")
221 val debuggerDisplayValue: T
222 @JvmName("getDebuggerDisplayValue") get() = next.withCurrent { it }.value
223 }
224
225 /**
226 * Create a instance of [MutableList]<T> that is observable and can be snapshot.
227 *
228 * @sample androidx.compose.runtime.samples.stateListSample
229 * @see mutableStateOf
230 * @see mutableListOf
231 * @see MutableList
232 * @see Snapshot.takeSnapshot
233 */
mutableStateListOfnull234 @StateFactoryMarker fun <T> mutableStateListOf() = SnapshotStateList<T>()
235
236 /**
237 * Create an instance of [MutableList]<T> that is observable and can be snapshot.
238 *
239 * @see mutableStateOf
240 * @see mutableListOf
241 * @see MutableList
242 * @see Snapshot.takeSnapshot
243 */
244 @StateFactoryMarker
245 fun <T> mutableStateListOf(vararg elements: T) =
246 SnapshotStateList<T>().also { it.addAll(elements.toList()) }
247
248 /**
249 * Create an instance of [MutableList]<T> from a collection that is observable and can be snapshot.
250 */
<lambda>null251 fun <T> Collection<T>.toMutableStateList() = SnapshotStateList<T>().also { it.addAll(this) }
252
253 /**
254 * Create a instance of [MutableMap]<K, V> that is observable and can be snapshot.
255 *
256 * @sample androidx.compose.runtime.samples.stateMapSample
257 * @see mutableStateOf
258 * @see mutableMapOf
259 * @see MutableMap
260 * @see Snapshot.takeSnapshot
261 */
mutableStateMapOfnull262 @StateFactoryMarker fun <K, V> mutableStateMapOf() = SnapshotStateMap<K, V>()
263
264 /**
265 * Create a instance of [MutableMap]<K, V> that is observable and can be snapshot.
266 *
267 * @see mutableStateOf
268 * @see mutableMapOf
269 * @see MutableMap
270 * @see Snapshot.takeSnapshot
271 */
272 @StateFactoryMarker
273 fun <K, V> mutableStateMapOf(vararg pairs: Pair<K, V>) =
274 SnapshotStateMap<K, V>().apply { putAll(pairs.toMap()) }
275
276 /**
277 * Create an instance of [MutableMap]<K, V> from a collection of pairs that is observable and can be
278 * snapshot.
279 */
280 @Suppress("unused")
toMutableStateMapnull281 fun <K, V> Iterable<Pair<K, V>>.toMutableStateMap() =
282 SnapshotStateMap<K, V>().also { it.putAll(this.toMap()) }
283
284 /**
285 * Create a instance of [MutableSet]<T> that is observable and can be snapshot.
286 *
287 * @sample androidx.compose.runtime.samples.stateListSample
288 * @see mutableStateOf
289 * @see mutableSetOf
290 * @see MutableSet
291 * @see Snapshot.takeSnapshot
292 */
mutableStateSetOfnull293 @StateFactoryMarker fun <T> mutableStateSetOf() = SnapshotStateSet<T>()
294
295 /**
296 * Create an instance of [MutableSet]<T> that is observable and can be snapshot.
297 *
298 * @see mutableStateOf
299 * @see mutableSetOf
300 * @see MutableSet
301 * @see Snapshot.takeSnapshot
302 */
303 @StateFactoryMarker
304 fun <T> mutableStateSetOf(vararg elements: T) =
305 SnapshotStateSet<T>().also { it.addAll(elements.toSet()) }
306
307 /**
308 * [remember] a [mutableStateOf] [newValue] and update its value to [newValue] on each recomposition
309 * of the [rememberUpdatedState] call.
310 *
311 * [rememberUpdatedState] should be used when parameters or values computed during composition are
312 * referenced by a long-lived lambda or object expression. Recomposition will update the resulting
313 * [State] without recreating the long-lived lambda or object, allowing that object to persist
314 * without cancelling and resubscribing, or relaunching a long-lived operation that may be expensive
315 * or prohibitive to recreate and restart. This may be common when working with [DisposableEffect]
316 * or [LaunchedEffect], for example:
317 *
318 * @sample androidx.compose.runtime.samples.rememberUpdatedStateSampleWithDisposableEffect
319 *
320 * [LaunchedEffect]s often describe state machines that should not be reset and restarted if a
321 * parameter or event callback changes, but they should have the current value available when
322 * needed. For example:
323 *
324 * @sample androidx.compose.runtime.samples.rememberUpdatedStateSampleWithLaunchedEffect
325 *
326 * By using [rememberUpdatedState] a composable function can update these operations in progress.
327 */
328 @Composable
rememberUpdatedStatenull329 fun <T> rememberUpdatedState(newValue: T): State<T> =
330 remember { mutableStateOf(newValue) }.apply { value = newValue }
331