0
# Composition and Context
1
2
Core composition APIs for building composable functions and managing implicit context propagation through the composition tree. These APIs form the foundation of the Compose runtime system.
3
4
## Capabilities
5
6
### Composable Functions
7
8
The fundamental building blocks of Compose UI.
9
10
```kotlin { .api }
11
/**
12
* Marks functions as composable, enabling them to call other composable functions
13
* and participate in the composition process
14
*/
15
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.TYPE)
16
@Retention(AnnotationRetention.BINARY)
17
annotation class Composable
18
```
19
20
**Usage Examples:**
21
22
```kotlin
23
import androidx.compose.runtime.Composable
24
25
// Basic composable function
26
@Composable
27
fun Greeting(name: String) {
28
Text("Hello, $name!")
29
}
30
31
// Composable with parameters and state
32
@Composable
33
fun Counter(initialValue: Int = 0) {
34
var count by remember { mutableStateOf(initialValue) }
35
36
Column {
37
Text("Count: $count")
38
Button(onClick = { count++ }) {
39
Text("Increment")
40
}
41
}
42
}
43
44
// Composable that accepts other composables
45
@Composable
46
fun Card(
47
modifier: Modifier = Modifier,
48
content: @Composable () -> Unit
49
) {
50
Surface(modifier = modifier) {
51
content()
52
}
53
}
54
```
55
56
### Memory and Caching
57
58
Remember values across recompositions with the remember API.
59
60
```kotlin { .api }
61
/**
62
* Remembers a value across recompositions
63
* The value is recalculated only on first composition or when keys change
64
* @param calculation Function to create the remembered value
65
* @return The remembered value
66
*/
67
@Composable
68
fun <T> remember(calculation: () -> T): T
69
70
/**
71
* Remembers a value with a single dependency key
72
* @param key1 Dependency key - value is recalculated when this changes
73
* @param calculation Function to create the remembered value
74
* @return The remembered value
75
*/
76
@Composable
77
fun <T> remember(key1: Any?, calculation: () -> T): T
78
79
/**
80
* Remembers a value with multiple dependency keys
81
*/
82
@Composable
83
fun <T> remember(key1: Any?, key2: Any?, calculation: () -> T): T
84
85
@Composable
86
fun <T> remember(key1: Any?, key2: Any?, key3: Any?, calculation: () -> T): T
87
88
/**
89
* Remembers a value with variable number of dependency keys
90
*/
91
@Composable
92
fun <T> remember(vararg keys: Any?, calculation: () -> T): T
93
```
94
95
**Usage Examples:**
96
97
```kotlin
98
@Composable
99
fun RememberExample(userId: String, settings: AppSettings) {
100
// Remember expensive computation
101
val processedData = remember(userId) {
102
expensiveDataProcessing(userId)
103
}
104
105
// Remember stateful objects
106
val listState = remember { LazyListState() }
107
val focusRequester = remember { FocusRequester() }
108
109
// Remember with multiple keys
110
val cachedResult = remember(userId, settings.cacheEnabled) {
111
if (settings.cacheEnabled) {
112
getCachedData(userId)
113
} else {
114
getServerData(userId)
115
}
116
}
117
118
// Remember lambda functions to avoid recreating them
119
val onItemClick = remember(userId) {
120
{ itemId: String -> handleItemClick(userId, itemId) }
121
}
122
}
123
```
124
125
### Composition Locals
126
127
Provide implicit values that can be accessed by any composable in the subtree.
128
129
```kotlin { .api }
130
/**
131
* Provides values to CompositionLocal instances for the content block
132
* @param values Provided values as key-value pairs
133
* @param content Composable content that can access the provided values
134
*/
135
@Composable
136
fun CompositionLocalProvider(
137
vararg values: ProvidedValue<*>,
138
content: @Composable () -> Unit
139
)
140
141
/**
142
* Abstract base class for CompositionLocal
143
*/
144
abstract class CompositionLocal<T> {
145
/**
146
* Current value of this CompositionLocal
147
*/
148
val current: T
149
@Composable get
150
151
/**
152
* Provides a value for this CompositionLocal
153
*/
154
infix fun provides(value: T): ProvidedValue<T>
155
}
156
157
/**
158
* CompositionLocal that never changes - more efficient for static values
159
*/
160
abstract class StaticProvidableCompositionLocal<T> : CompositionLocal<T>()
161
162
/**
163
* CompositionLocal that can change and notifies composables when it does
164
*/
165
abstract class DynamicProvidableCompositionLocal<T> : CompositionLocal<T>()
166
167
/**
168
* Base class for CompositionLocal instances that can provide values
169
*/
170
abstract class ProvidableCompositionLocal<T> : CompositionLocal<T>()
171
172
/**
173
* Represents a key-value pair for providing CompositionLocal values
174
*/
175
data class ProvidedValue<T>(
176
val compositionLocal: CompositionLocal<T>,
177
val value: T
178
)
179
```
180
181
**Usage Examples:**
182
183
```kotlin
184
// Define composition locals
185
val LocalUserPreferences = compositionLocalOf { UserPreferences() }
186
val LocalTheme = staticCompositionLocalOf { AppTheme.Light }
187
188
@Composable
189
fun App() {
190
val userPrefs = remember { loadUserPreferences() }
191
val currentTheme = remember(userPrefs.themeMode) {
192
getThemeForMode(userPrefs.themeMode)
193
}
194
195
CompositionLocalProvider(
196
LocalUserPreferences provides userPrefs,
197
LocalTheme provides currentTheme
198
) {
199
MainScreen()
200
}
201
}
202
203
@Composable
204
fun MainScreen() {
205
// Access composition locals
206
val theme = LocalTheme.current
207
val userPrefs = LocalUserPreferences.current
208
209
Surface(color = theme.backgroundColor) {
210
if (userPrefs.showWelcome) {
211
WelcomeMessage()
212
}
213
ContentArea()
214
}
215
}
216
217
@Composable
218
fun WelcomeMessage() {
219
val userPrefs = LocalUserPreferences.current
220
Text(
221
text = "Welcome, ${userPrefs.userName}!",
222
color = LocalTheme.current.textColor
223
)
224
}
225
```
226
227
### Creating Composition Locals
228
229
Define your own CompositionLocal instances.
230
231
```kotlin { .api }
232
/**
233
* Creates a CompositionLocal with change notifications
234
* More overhead but allows dynamic updates
235
* @param policy Mutation policy for detecting changes
236
* @param defaultFactory Optional factory for default value
237
* @return ProvidableComposionLocal instance
238
*/
239
fun <T> compositionLocalOf(
240
policy: SnapshotMutationPolicy<T> = structuralEqualityPolicy(),
241
defaultFactory: (() -> T)? = null
242
): ProvidableCompositionLocal<T>
243
244
/**
245
* Creates a CompositionLocal that never changes
246
* More efficient for static values
247
* @param defaultFactory Optional factory for default value
248
* @return ProvidableCompositionLocal instance
249
*/
250
fun <T> staticCompositionLocalOf(
251
defaultFactory: (() -> T)? = null
252
): ProvidableCompositionLocal<T>
253
```
254
255
**Usage Examples:**
256
257
```kotlin
258
// Dynamic composition local (can change)
259
val LocalUser = compositionLocalOf<User?> { null }
260
261
// Static composition local (never changes)
262
val LocalAppConfig = staticCompositionLocalOf {
263
AppConfig.default()
264
}
265
266
// Composition local with default value
267
val LocalAnalytics = compositionLocalOf {
268
NoOpAnalytics() // Default implementation
269
}
270
271
@Composable
272
fun MyApp() {
273
val currentUser = remember { getCurrentUser() }
274
275
CompositionLocalProvider(
276
LocalUser provides currentUser,
277
LocalAnalytics provides RealAnalytics()
278
) {
279
NavigationHost()
280
}
281
}
282
283
@Composable
284
fun UserProfile() {
285
val user = LocalUser.current
286
val analytics = LocalAnalytics.current
287
288
if (user != null) {
289
LaunchedEffect(user.id) {
290
analytics.trackScreenView("UserProfile", user.id)
291
}
292
293
Column {
294
Text("Name: ${user.name}")
295
Text("Email: ${user.email}")
296
}
297
} else {
298
LoginPrompt()
299
}
300
}
301
```
302
303
### Composition Tree Navigation
304
305
Access and manipulate the composition tree structure.
306
307
```kotlin { .api }
308
/**
309
* Returns the current Composer instance
310
*/
311
val currentComposer: Composer
312
@Composable get
313
314
/**
315
* Returns the current recompose scope
316
*/
317
val currentRecomposeScope: RecomposeScope
318
@Composable get
319
320
/**
321
* Interface for controlling recomposition
322
*/
323
interface RecomposeScope {
324
/**
325
* Invalidates this scope, causing it to recompose
326
*/
327
fun invalidate()
328
}
329
330
/**
331
* The Composer is the interface between the composable functions and the composition tree
332
*/
333
interface Composer {
334
/**
335
* Whether the composer is currently inserting content into the composition
336
*/
337
val inserting: Boolean
338
339
/**
340
* Whether the composer is currently skipping content during recomposition
341
*/
342
val skipping: Boolean
343
}
344
```
345
346
**Usage Examples:**
347
348
```kotlin
349
@Composable
350
fun DebuggingExample() {
351
val scope = currentRecomposeScope
352
353
Button(
354
onClick = {
355
// Force recomposition of this scope
356
scope.invalidate()
357
}
358
) {
359
Text("Force Recompose")
360
}
361
}
362
363
@Composable
364
fun ComposerExample() {
365
val composer = currentComposer
366
367
// Access composer information (advanced usage)
368
SideEffect {
369
println("Composer inserting: ${composer.inserting}")
370
println("Composer skipping: ${composer.skipping}")
371
}
372
}
373
```
374
375
### Composition Annotations
376
377
Control composition behavior with annotations.
378
379
```kotlin { .api }
380
/**
381
* Marks composables that shouldn't restart on recomposition
382
* Use for performance optimization when restart isn't needed
383
*/
384
@Target(AnnotationTarget.FUNCTION)
385
@Retention(AnnotationRetention.BINARY)
386
annotation class NonRestartableComposable
387
388
/**
389
* Marks composables that only read values and don't participate in recomposition
390
* Used for optimization - these composables won't be recomposed
391
*/
392
@Target(AnnotationTarget.FUNCTION)
393
@Retention(AnnotationRetention.BINARY)
394
annotation class ReadOnlyComposable
395
396
/**
397
* Marks APIs used by the Compose compiler
398
* These are internal APIs not intended for direct use
399
*/
400
@Target(
401
AnnotationTarget.CLASS,
402
AnnotationTarget.FUNCTION,
403
AnnotationTarget.PROPERTY,
404
AnnotationTarget.TYPEALIAS
405
)
406
@Retention(AnnotationRetention.BINARY)
407
annotation class ComposeCompilerApi
408
```
409
410
**Usage Examples:**
411
412
```kotlin
413
// Non-restartable composable for performance
414
@NonRestartableComposable
415
@Composable
416
fun StaticHeader() {
417
Text("App Title") // Never changes, doesn't need restart
418
}
419
420
// Read-only composable that doesn't trigger recomposition
421
@ReadOnlyComposable
422
@Composable
423
fun currentPlatform(): Platform {
424
return Platform.current // Static value
425
}
426
427
@Composable
428
fun OptimizedScreen() {
429
StaticHeader() // Won't restart on recomposition
430
431
val platform = currentPlatform() // Won't cause recomposition
432
433
PlatformSpecificContent(platform)
434
}
435
```
436
437
## Advanced Composition Patterns
438
439
### Custom Remember Implementation
440
441
Create custom remember-like behavior.
442
443
```kotlin
444
@Composable
445
fun <T> rememberWithLifecycle(
446
lifecycle: Lifecycle,
447
calculation: () -> T
448
): T {
449
return remember(lifecycle.currentState) {
450
calculation()
451
}
452
}
453
454
@Composable
455
fun <T> rememberCached(
456
key: String,
457
calculation: () -> T
458
): T {
459
return remember(key) {
460
cache.get(key) ?: calculation().also { cache.put(key, it) }
461
}
462
}
463
```
464
465
### Conditional Composition
466
467
Patterns for conditional composition based on state.
468
469
```kotlin
470
@Composable
471
fun ConditionalComposition(showDetails: Boolean) {
472
if (showDetails) {
473
DetailedView()
474
} else {
475
SummaryView()
476
}
477
}
478
479
@Composable
480
fun <T> ConditionalProvider(
481
condition: Boolean,
482
value: T,
483
content: @Composable () -> Unit
484
) {
485
if (condition) {
486
CompositionLocalProvider(LocalMyValue provides value) {
487
content()
488
}
489
} else {
490
content()
491
}
492
}
493
```