0
# CompositionLocal
1
2
Context-like data flow mechanism for passing data implicitly down the composition tree without explicit parameter passing. CompositionLocal enables sharing data across the entire composition hierarchy while maintaining type safety.
3
4
## Capabilities
5
6
### CompositionLocal Creation
7
8
Functions for creating CompositionLocal instances with different update policies.
9
10
```kotlin { .api }
11
/**
12
* Creates a CompositionLocal that uses structural equality for change detection
13
* @param defaultFactory Optional factory for default value when no provider is found
14
* @return ProvidableCompositionLocal instance
15
*/
16
fun <T> compositionLocalOf(defaultFactory: (() -> T)? = null): ProvidableCompositionLocal<T>
17
18
/**
19
* Creates a CompositionLocal that uses referential equality for change detection
20
* Optimized for values that rarely change
21
* @param defaultFactory Optional factory for default value when no provider is found
22
* @return ProvidableCompositionLocal instance
23
*/
24
fun <T> staticCompositionLocalOf(defaultFactory: (() -> T)? = null): ProvidableCompositionLocal<T>
25
```
26
27
**Usage Examples:**
28
29
```kotlin
30
// Theme CompositionLocal
31
val LocalAppTheme = compositionLocalOf<AppTheme> {
32
error("No theme provided")
33
}
34
35
// User CompositionLocal with default
36
val LocalCurrentUser = compositionLocalOf { User.guest() }
37
38
// Static CompositionLocal for rarely-changing configuration
39
val LocalAppConfiguration = staticCompositionLocalOf {
40
AppConfiguration.default()
41
}
42
43
data class AppTheme(
44
val colors: ColorScheme,
45
val typography: Typography,
46
val spacing: Spacing
47
)
48
49
data class User(val id: String, val name: String) {
50
companion object {
51
fun guest() = User("guest", "Guest User")
52
}
53
}
54
```
55
56
### CompositionLocal Classes
57
58
Core classes and interfaces for the CompositionLocal system.
59
60
```kotlin { .api }
61
/**
62
* Base class for CompositionLocal providing read access
63
* @param T The type of value provided by this CompositionLocal
64
*/
65
abstract class CompositionLocal<T>(defaultFactory: (() -> T)?) {
66
/**
67
* Current value of this CompositionLocal in the composition
68
* Throws if no provider is found and no default factory was provided
69
*/
70
val current: T
71
@Composable get
72
73
/**
74
* Current value or null if no provider is found
75
*/
76
val currentOrNull: T?
77
@Composable get
78
}
79
80
/**
81
* CompositionLocal that can provide values to child compositions
82
*/
83
abstract class ProvidableCompositionLocal<T> : CompositionLocal<T> {
84
/**
85
* Creates a ProvidedValue for use with CompositionLocalProvider
86
* @param value The value to provide
87
* @return ProvidedValue instance
88
*/
89
infix fun provides(value: T): ProvidedValue<T>
90
91
/**
92
* Creates a ProvidedValue with a computed value
93
* @param value Function that computes the value
94
* @return ProvidedValue instance
95
*/
96
infix fun providesDefault(value: () -> T): ProvidedValue<T>
97
}
98
99
/**
100
* Represents a value provided to a CompositionLocal
101
*/
102
class ProvidedValue<T> internal constructor(
103
val compositionLocal: CompositionLocal<T>,
104
val value: T,
105
val canOverride: Boolean
106
)
107
```
108
109
### CompositionLocalProvider
110
111
Function for providing values to CompositionLocals.
112
113
```kotlin { .api }
114
/**
115
* Provides values to CompositionLocals for the content lambda
116
* @param values Variable number of ProvidedValue instances
117
* @param content Composable content that can access the provided values
118
*/
119
@Composable
120
fun CompositionLocalProvider(
121
vararg values: ProvidedValue<*>,
122
content: @Composable () -> Unit
123
): Unit
124
```
125
126
**Usage Examples:**
127
128
```kotlin
129
@Composable
130
fun App() {
131
val theme = remember { AppTheme.default() }
132
val user = remember { getCurrentUser() }
133
134
CompositionLocalProvider(
135
LocalAppTheme provides theme,
136
LocalCurrentUser provides user
137
) {
138
MainScreen() // Can access theme and user via CompositionLocal
139
}
140
}
141
142
@Composable
143
fun MainScreen() {
144
// Access provided values
145
val theme = LocalAppTheme.current
146
val user = LocalCurrentUser.current
147
148
Column(
149
modifier = Modifier.background(theme.colors.background)
150
) {
151
Text(
152
text = "Welcome, ${user.name}!",
153
style = theme.typography.headlineMedium,
154
color = theme.colors.onBackground
155
)
156
157
UserContent() // Can also access the same CompositionLocals
158
}
159
}
160
```
161
162
### Reading CompositionLocal Values
163
164
Methods for safely accessing CompositionLocal values.
165
166
```kotlin { .api }
167
/**
168
* Extension property to get current value (same as .current)
169
*/
170
val <T> CompositionLocal<T>.current: T
171
@Composable get
172
173
/**
174
* Extension property to get current value or null if not provided
175
*/
176
val <T> CompositionLocal<T>.currentOrNull: T?
177
@Composable get
178
```
179
180
**Usage Examples:**
181
182
```kotlin
183
@Composable
184
fun SafeCompositionLocalAccess() {
185
// Safe access with null check
186
val user = LocalCurrentUser.currentOrNull
187
188
if (user != null) {
189
Text("Hello, ${user.name}")
190
} else {
191
Text("Please log in")
192
}
193
194
// Direct access (throws if not provided and no default)
195
val theme = LocalAppTheme.current
196
197
// Using in conditional logic
198
val isGuest = LocalCurrentUser.currentOrNull?.id == "guest"
199
}
200
```
201
202
## Common CompositionLocal Patterns
203
204
### Nested Providers
205
206
```kotlin
207
@Composable
208
fun NestedProvidersExample() {
209
val lightTheme = AppTheme.light()
210
211
CompositionLocalProvider(LocalAppTheme provides lightTheme) {
212
Column {
213
Text("Light theme content")
214
215
// Override theme for specific section
216
val darkTheme = AppTheme.dark()
217
CompositionLocalProvider(LocalAppTheme provides darkTheme) {
218
Text("Dark theme content")
219
220
NestedContent() // Uses dark theme
221
}
222
223
Text("Back to light theme")
224
}
225
}
226
}
227
```
228
229
### Dynamic Value Updates
230
231
```kotlin
232
@Composable
233
fun DynamicCompositionLocalExample() {
234
var isDarkMode by remember { mutableStateOf(false) }
235
236
val theme = if (isDarkMode) AppTheme.dark() else AppTheme.light()
237
238
CompositionLocalProvider(LocalAppTheme provides theme) {
239
Column {
240
Switch(
241
checked = isDarkMode,
242
onCheckedChange = { isDarkMode = it }
243
)
244
245
ThemedContent() // Automatically updates when theme changes
246
}
247
}
248
}
249
```
250
251
### Custom CompositionLocal Scope
252
253
```kotlin
254
// Custom scope for feature-specific data
255
val LocalFeatureConfig = compositionLocalOf<FeatureConfig> {
256
FeatureConfig.default()
257
}
258
259
@Composable
260
fun FeatureScope(
261
config: FeatureConfig,
262
content: @Composable () -> Unit
263
) {
264
CompositionLocalProvider(LocalFeatureConfig provides config) {
265
content()
266
}
267
}
268
269
@Composable
270
fun FeatureScreen() {
271
FeatureScope(config = FeatureConfig.forUser(currentUser)) {
272
FeatureContent() // Can access LocalFeatureConfig
273
}
274
}
275
```
276
277
## iOS Platform Integration
278
279
### iOS-Specific CompositionLocals
280
281
```kotlin
282
// iOS-specific configuration
283
val LocalIOSConfiguration = compositionLocalOf<IOSConfiguration> {
284
error("iOS configuration not provided")
285
}
286
287
val LocalUIViewController = compositionLocalOf<UIViewController?> { null }
288
289
data class IOSConfiguration(
290
val statusBarStyle: UIStatusBarStyle,
291
val interfaceOrientation: UIInterfaceOrientation,
292
val safeAreaInsets: EdgeInsets
293
)
294
295
@Composable
296
fun IOSApp(viewController: UIViewController) {
297
val iosConfig = remember {
298
IOSConfiguration(
299
statusBarStyle = UIStatusBarStyle.default,
300
interfaceOrientation = UIInterfaceOrientation.portrait,
301
safeAreaInsets = viewController.view.safeAreaInsets.toEdgeInsets()
302
)
303
}
304
305
CompositionLocalProvider(
306
LocalUIViewController provides viewController,
307
LocalIOSConfiguration provides iosConfig
308
) {
309
AppContent()
310
}
311
}
312
```
313
314
### Integration with UIKit
315
316
```kotlin
317
@Composable
318
fun UIKitIntegrationExample() {
319
val viewController = LocalUIViewController.currentOrNull
320
val iosConfig = LocalIOSConfiguration.current
321
322
LaunchedEffect(iosConfig.statusBarStyle) {
323
viewController?.setNeedsStatusBarAppearanceUpdate()
324
}
325
326
// Use iOS configuration in Composable
327
Column(
328
modifier = Modifier.padding(iosConfig.safeAreaInsets)
329
) {
330
// Content respects safe area insets
331
}
332
}
333
```
334
335
## Advanced Patterns
336
337
### CompositionLocal Transformation
338
339
```kotlin
340
@Composable
341
fun TransformCompositionLocalExample() {
342
val baseTheme = LocalAppTheme.current
343
344
// Create derived theme
345
val dialogTheme = remember(baseTheme) {
346
baseTheme.copy(
347
colors = baseTheme.colors.copy(
348
surface = baseTheme.colors.surfaceVariant
349
)
350
)
351
}
352
353
CompositionLocalProvider(LocalAppTheme provides dialogTheme) {
354
DialogContent() // Uses modified theme
355
}
356
}
357
```
358
359
### Conditional Provision
360
361
```kotlin
362
@Composable
363
fun ConditionalProvisionExample(user: User?) {
364
if (user != null) {
365
CompositionLocalProvider(LocalCurrentUser provides user) {
366
AuthenticatedContent()
367
}
368
} else {
369
UnauthenticatedContent() // LocalCurrentUser not provided
370
}
371
}
372
```
373
374
### Multiple Providers with Same Local
375
376
```kotlin
377
@Composable
378
fun MultipleProvidersExample() {
379
val adminTheme = AppTheme.admin()
380
val userTheme = AppTheme.user()
381
382
Row {
383
CompositionLocalProvider(LocalAppTheme provides adminTheme) {
384
AdminPanel()
385
}
386
387
CompositionLocalProvider(LocalAppTheme provides userTheme) {
388
UserPanel()
389
}
390
}
391
}
392
```
393
394
## Performance Considerations
395
396
- **Static vs Dynamic**: Use `staticCompositionLocalOf` for rarely-changing values to optimize recomposition
397
- **Scope Minimization**: Provide CompositionLocal values as close as possible to where they're needed
398
- **Value Stability**: Ensure provided values are stable to avoid unnecessary recomposition
399
- **Default Factories**: Use default factories sparingly as they're called on every access when no provider exists
400
- **Nested Providers**: Minimize deep nesting of providers to reduce composition overhead