0
# Scope Management
1
2
Advanced scope management capabilities allowing creation and management of Koin scopes with automatic lifecycle handling tied to Compose composition lifecycle. Scopes are automatically closed when compositions are forgotten or abandoned.
3
4
## Capabilities
5
6
### KoinScope with Scope Definition
7
8
Create a Koin scope using a lambda definition and automatically manage its lifecycle with Compose.
9
10
```kotlin { .api }
11
/**
12
* Create Koin Scope & close it when Composition is on onForgotten/onAbandoned
13
*
14
* @param scopeDefinition - lambda to define scope
15
* @param content - composable content that uses the scope
16
*/
17
@Composable
18
@KoinExperimentalAPI
19
fun KoinScope(
20
scopeDefinition: Koin.() -> Scope,
21
content: @Composable () -> Unit
22
)
23
```
24
25
**Usage Examples:**
26
27
```kotlin
28
import org.koin.compose.getKoin
29
import org.koin.compose.scope.KoinScope
30
31
@Composable
32
fun UserProfileScreen(userId: String) {
33
KoinScope(
34
scopeDefinition = {
35
createScope<UserProfileScope>("user-profile-$userId")
36
}
37
) {
38
// Content within this scope can access UserProfile-scoped dependencies
39
UserProfileContent()
40
UserSettingsPanel()
41
}
42
// Scope is automatically closed when this composition is forgotten
43
}
44
45
@Composable
46
fun SessionBasedScreen() {
47
val sessionId = remember { UUID.randomUUID().toString() }
48
49
KoinScope(
50
scopeDefinition = {
51
getOrCreateScope("session", StringQualifier(sessionId))
52
}
53
) {
54
// All composables here share the same session scope
55
HeaderComponent()
56
MainContent()
57
FooterComponent()
58
}
59
}
60
```
61
62
### KoinScope with Typed Scope
63
64
Create a Koin scope from a specific type with automatic lifecycle management.
65
66
```kotlin { .api }
67
/**
68
* Create Koin Scope from type T & close it when Composition is on onForgotten/onAbandoned
69
*
70
* @param scopeID - unique identifier for the scope
71
* @param content - composable content that uses the scope
72
*/
73
@Composable
74
@KoinExperimentalAPI
75
inline fun <reified T : Any> KoinScope(
76
scopeID: ScopeID,
77
noinline content: @Composable () -> Unit
78
)
79
```
80
81
**Usage Examples:**
82
83
```kotlin
84
import org.koin.compose.scope.KoinScope
85
86
// Define scope types
87
class UserScope
88
class FeatureScope
89
class ActivityScope
90
91
@Composable
92
fun UserDashboard(userId: String) {
93
KoinScope<UserScope>(
94
scopeID = "user-$userId"
95
) {
96
// This content has access to UserScope-scoped dependencies
97
UserHeader()
98
UserStats()
99
UserActions()
100
}
101
}
102
103
@Composable
104
fun FeatureScreen(featureId: String) {
105
KoinScope<FeatureScope>(
106
scopeID = "feature-$featureId"
107
) {
108
FeatureContent()
109
}
110
}
111
```
112
113
### KoinScope with Qualifier
114
115
Create a Koin scope with both scope ID and qualifier for more specific scope management.
116
117
```kotlin { .api }
118
/**
119
* Create Koin Scope from type with qualifier & close it when Composition is on onForgotten/onAbandoned
120
*
121
* @param scopeID - unique identifier for the scope
122
* @param scopeQualifier - qualifier for the scope
123
* @param content - composable content that uses the scope
124
*/
125
@Composable
126
@KoinExperimentalAPI
127
fun KoinScope(
128
scopeID: ScopeID,
129
scopeQualifier: Qualifier,
130
noinline content: @Composable () -> Unit
131
)
132
```
133
134
**Usage Examples:**
135
136
```kotlin
137
import org.koin.compose.scope.KoinScope
138
import org.koin.core.qualifier.named
139
140
@Composable
141
fun MultiTenantScreen(tenantId: String, environment: String) {
142
KoinScope(
143
scopeID = "tenant-$tenantId",
144
scopeQualifier = named(environment) // "dev", "staging", "prod"
145
) {
146
// Content uses tenant-specific, environment-qualified scope
147
TenantHeader()
148
TenantContent()
149
}
150
}
151
152
@Composable
153
fun GameSession(gameId: String, difficulty: String) {
154
KoinScope(
155
scopeID = "game-$gameId",
156
scopeQualifier = named("difficulty-$difficulty")
157
) {
158
GameBoard()
159
GameStats()
160
GameControls()
161
}
162
}
163
```
164
165
### rememberKoinScope
166
167
Remember a Koin scope and handle its lifecycle with Compose's remember system.
168
169
```kotlin { .api }
170
/**
171
* Remember Koin Scope & run CompositionKoinScopeLoader to handle scope closure
172
*
173
* @param scope - Koin scope to remember and manage
174
* @return The managed scope instance
175
*/
176
@Composable
177
@KoinExperimentalAPI
178
fun rememberKoinScope(scope: Scope): Scope
179
```
180
181
**Usage Examples:**
182
183
```kotlin
184
import org.koin.compose.getKoin
185
import org.koin.compose.scope.rememberKoinScope
186
187
@Composable
188
fun CustomScopeManagement(scopeId: String) {
189
val koin = getKoin()
190
191
// Create scope manually
192
val customScope = remember(scopeId) {
193
koin.createScope<MyCustomScope>(scopeId)
194
}
195
196
// Remember and manage its lifecycle
197
val managedScope = rememberKoinScope(customScope)
198
199
// Use the managed scope
200
val scopedService = remember(managedScope) {
201
managedScope.get<MyScopedService>()
202
}
203
204
MyCustomContent(scopedService)
205
}
206
207
@Composable
208
fun ConditionalScope(shouldCreateScope: Boolean) {
209
if (shouldCreateScope) {
210
val scope = remember {
211
getKoin().createScope<ConditionalScope>("conditional")
212
}
213
214
val managedScope = rememberKoinScope(scope)
215
216
ConditionalContent(managedScope)
217
} else {
218
DefaultContent()
219
}
220
}
221
```
222
223
## Lifecycle Management
224
225
### Automatic Scope Closure
226
227
All KoinScope composables automatically close their scopes when:
228
229
- **onForgotten**: Composition is forgotten (e.g., navigating away)
230
- **onAbandoned**: Composition is abandoned (e.g., configuration change)
231
232
```kotlin
233
// Internal lifecycle management
234
class CompositionKoinScopeLoader(val scope: Scope) : RememberObserver {
235
override fun onRemembered() {
236
// Scope is active
237
}
238
239
override fun onForgotten() {
240
close()
241
}
242
243
override fun onAbandoned() {
244
close()
245
}
246
247
private fun close() {
248
if (!scope.isRoot && !scope.closed) {
249
scope.close()
250
}
251
}
252
}
253
```
254
255
### Manual Scope Management
256
257
```kotlin
258
@Composable
259
fun ManualScopeLifecycle() {
260
val scope = remember { getKoin().createScope<MyScope>("manual") }
261
262
// Manual lifecycle control
263
DisposableEffect(scope) {
264
onDispose {
265
if (!scope.closed) {
266
scope.close()
267
}
268
}
269
}
270
271
// Don't use rememberKoinScope if managing manually
272
ScopeContent(scope)
273
}
274
```
275
276
## Scope Definition Patterns
277
278
### User-Based Scopes
279
280
```kotlin
281
@Composable
282
fun UserScopedScreen(user: User) {
283
KoinScope<UserScope>(
284
scopeID = "user-${user.id}"
285
) {
286
// All user-related components share this scope
287
UserProfile(user)
288
UserPreferences(user)
289
UserActivity(user)
290
}
291
}
292
```
293
294
### Feature-Based Scopes
295
296
```kotlin
297
@Composable
298
fun FeatureModule(featureFlag: String) {
299
KoinScope(
300
scopeDefinition = {
301
createScope<FeatureScope>("feature-$featureFlag")
302
}
303
) {
304
if (featureFlag == "new_ui") {
305
NewUIComponents()
306
} else {
307
LegacyUIComponents()
308
}
309
}
310
}
311
```
312
313
### Navigation-Based Scopes
314
315
```kotlin
316
@Composable
317
fun NavigationScope(route: String) {
318
KoinScope(
319
scopeID = "nav-$route",
320
scopeQualifier = named("navigation")
321
) {
322
when (route) {
323
"home" -> HomeScreen()
324
"profile" -> ProfileScreen()
325
"settings" -> SettingsScreen()
326
}
327
}
328
}
329
```
330
331
## Scope Injection Within Scoped Content
332
333
When inside a KoinScope, use `koinInject` normally - it will automatically use the current scope:
334
335
```kotlin
336
@Composable
337
fun UserProfileScreen(userId: String) {
338
KoinScope<UserScope>(scopeID = "user-$userId") {
339
UserProfileContent() // Can inject UserScope dependencies
340
}
341
}
342
343
@Composable
344
fun UserProfileContent() {
345
// This will inject from the UserScope
346
val userRepository: UserRepository = koinInject()
347
val userPreferences: UserPreferences = koinInject()
348
349
// Content using scoped dependencies
350
Text("User: ${userRepository.getCurrentUser().name}")
351
}
352
```
353
354
## Error Handling
355
356
### Scope Creation Failures
357
358
```kotlin
359
@Composable
360
fun SafeScopeCreation(scopeId: String) {
361
try {
362
KoinScope<MyScope>(scopeID = scopeId) {
363
ScopeContent()
364
}
365
} catch (e: ScopeAlreadyCreatedException) {
366
// Handle duplicate scope creation
367
Text("Scope already exists: $scopeId")
368
} catch (e: Exception) {
369
// Handle other scope creation errors
370
Text("Failed to create scope: ${e.message}")
371
}
372
}
373
```
374
375
### Closed Scope Access
376
377
```kotlin
378
@Composable
379
fun ScopeAwareContent() {
380
val scope = currentKoinScope()
381
382
if (scope.closed) {
383
Text("Scope is closed")
384
} else {
385
try {
386
val service: MyService = koinInject()
387
ServiceContent(service)
388
} catch (e: ClosedScopeException) {
389
Text("Scope was closed during access")
390
}
391
}
392
}
393
```
394
395
## Core Types
396
397
```kotlin { .api }
398
// Scope types
399
typealias ScopeID = String
400
401
interface Scope {
402
val id: String
403
val isRoot: Boolean
404
val closed: Boolean
405
val logger: Logger
406
fun close()
407
}
408
409
// Scope management
410
interface RememberObserver {
411
fun onRemembered()
412
fun onForgotten()
413
fun onAbandoned()
414
}
415
416
// Qualifiers for scope identification
417
interface Qualifier
418
class StringQualifier(val value: String) : Qualifier
419
fun named(name: String): Qualifier
420
```
421
422
## Best Practices
423
424
### Scope Naming
425
426
```kotlin
427
// ✅ Good: Descriptive, unique scope IDs
428
KoinScope<UserScope>("user-${user.id}")
429
KoinScope<FeatureScope>("feature-shopping-cart")
430
KoinScope<SessionScope>("session-${sessionId}")
431
432
// ❌ Poor: Generic, collision-prone scope IDs
433
KoinScope<UserScope>("scope")
434
KoinScope<FeatureScope>("temp")
435
```
436
437
### Scope Granularity
438
439
```kotlin
440
// ✅ Good: Appropriate scope granularity
441
@Composable
442
fun ShoppingFlow() {
443
KoinScope<ShoppingScope>("shopping-session") {
444
ProductCatalog()
445
ShoppingCart()
446
Checkout()
447
}
448
}
449
450
// ❌ Poor: Too many nested scopes
451
@Composable
452
fun OverScopedFlow() {
453
KoinScope<Scope1>("scope1") {
454
KoinScope<Scope2>("scope2") {
455
KoinScope<Scope3>("scope3") {
456
Content() // Too much nesting
457
}
458
}
459
}
460
}
461
```
462
463
### Scope Lifecycle Alignment
464
465
```kotlin
466
// ✅ Good: Scope lifetime matches business logic
467
@Composable
468
fun UserSession(user: User) {
469
// Scope lives for entire user session
470
KoinScope<UserScope>("user-${user.id}") {
471
UserDashboard()
472
}
473
}
474
475
// ✅ Good: Scope per business operation
476
@Composable
477
fun CheckoutFlow(cartId: String) {
478
// Scope lives for checkout process
479
KoinScope<CheckoutScope>("checkout-$cartId") {
480
CheckoutSteps()
481
}
482
}
483
```