0
# Module Management
1
2
Dynamic module loading and unloading capabilities with integration into Compose lifecycle for automatic cleanup. Enables loading Koin modules on-demand during composition and automatically unloading them when no longer needed.
3
4
## Capabilities
5
6
### rememberKoinModules
7
8
Load and remember Koin modules with automatic lifecycle management tied to Compose's remember system.
9
10
```kotlin { .api }
11
/**
12
* Load and remember Modules & run CompositionKoinModuleLoader to handle scope closure
13
*
14
* @param unloadOnForgotten : unload loaded modules on onForgotten event
15
* @param unloadOnAbandoned : unload loaded modules on onAbandoned event
16
* @param unloadModules : unload loaded modules on onForgotten or onAbandoned event
17
* @param modules : lambda providing list of modules to load
18
*/
19
@Composable
20
@KoinExperimentalAPI
21
inline fun rememberKoinModules(
22
unloadOnForgotten: Boolean? = null,
23
unloadOnAbandoned: Boolean? = null,
24
unloadModules: Boolean = false,
25
crossinline modules: @DisallowComposableCalls () -> List<Module> = { emptyList() }
26
)
27
```
28
29
**Usage Examples:**
30
31
```kotlin
32
import org.koin.compose.module.rememberKoinModules
33
import org.koin.dsl.module
34
35
@Composable
36
fun FeatureScreen(featureEnabled: Boolean) {
37
if (featureEnabled) {
38
// Load feature-specific modules when feature is enabled
39
rememberKoinModules(
40
unloadModules = true // Unload when composition is forgotten/abandoned
41
) {
42
listOf(
43
module {
44
single<FeatureService> { FeatureServiceImpl() }
45
single<FeatureRepository> { FeatureRepositoryImpl() }
46
},
47
module {
48
single<FeatureAnalytics> { FeatureAnalyticsImpl() }
49
}
50
)
51
}
52
53
FeatureContent()
54
} else {
55
DefaultContent()
56
}
57
}
58
59
@Composable
60
fun DynamicModules(userId: String?) {
61
// Load user-specific modules only when user is logged in
62
userId?.let { id ->
63
rememberKoinModules(
64
unloadOnForgotten = true,
65
unloadOnAbandoned = false
66
) {
67
listOf(
68
createUserModule(id),
69
createUserPreferencesModule(id)
70
)
71
}
72
}
73
74
UserInterface()
75
}
76
```
77
78
### Module Lifecycle Control
79
80
Fine-grained control over when modules are unloaded:
81
82
```kotlin
83
@Composable
84
fun ModuleLifecycleDemo() {
85
// Never unload automatically
86
rememberKoinModules {
87
listOf(persistentModule)
88
}
89
90
// Unload only when forgotten (e.g., navigation away)
91
rememberKoinModules(
92
unloadOnForgotten = true,
93
unloadOnAbandoned = false
94
) {
95
listOf(navigationScopedModule)
96
}
97
98
// Unload only when abandoned (e.g., configuration change)
99
rememberKoinModules(
100
unloadOnForgotten = false,
101
unloadOnAbandoned = true
102
) {
103
listOf(configurationSensitiveModule)
104
}
105
106
// Unload in both scenarios (shorthand)
107
rememberKoinModules(unloadModules = true) {
108
listOf(temporaryModule)
109
}
110
}
111
```
112
113
## Advanced Usage Patterns
114
115
### Conditional Module Loading
116
117
```kotlin
118
@Composable
119
fun ConditionalModules(
120
isDebugMode: Boolean,
121
featureFlags: Set<String>,
122
userTier: UserTier
123
) {
124
// Debug modules only in debug mode
125
if (isDebugMode) {
126
rememberKoinModules(unloadModules = true) {
127
listOf(debugModule, loggingModule)
128
}
129
}
130
131
// Feature-specific modules based on flags
132
rememberKoinModules(unloadModules = true) {
133
featureFlags.mapNotNull { flag ->
134
when (flag) {
135
"new_ui" -> newUIModule
136
"analytics" -> analyticsModule
137
"experimental" -> experimentalModule
138
else -> null
139
}
140
}
141
}
142
143
// Tier-based modules
144
rememberKoinModules(unloadModules = true) {
145
when (userTier) {
146
UserTier.FREE -> listOf(freeUserModule)
147
UserTier.PREMIUM -> listOf(freeUserModule, premiumModule)
148
UserTier.ENTERPRISE -> listOf(freeUserModule, premiumModule, enterpriseModule)
149
}
150
}
151
}
152
```
153
154
### Environment-Based Module Loading
155
156
```kotlin
157
@Composable
158
fun EnvironmentModules(environment: Environment) {
159
rememberKoinModules(unloadModules = true) {
160
when (environment) {
161
Environment.DEVELOPMENT -> listOf(
162
devApiModule,
163
mockDataModule,
164
debugToolsModule
165
)
166
Environment.STAGING -> listOf(
167
stagingApiModule,
168
realDataModule,
169
limitedDebugModule
170
)
171
Environment.PRODUCTION -> listOf(
172
prodApiModule,
173
realDataModule,
174
analyticsModule
175
)
176
}
177
}
178
}
179
```
180
181
### Dynamic User Context Modules
182
183
```kotlin
184
@Composable
185
fun UserContextModules(user: User?) {
186
// Base modules always loaded
187
rememberKoinModules {
188
listOf(baseAppModule, authModule)
189
}
190
191
// User-specific modules loaded when user is available
192
user?.let { currentUser ->
193
rememberKoinModules(unloadModules = true) {
194
buildList {
195
// User data module
196
add(createUserDataModule(currentUser.id))
197
198
// Role-based modules
199
if (currentUser.isAdmin) {
200
add(adminModule)
201
}
202
203
if (currentUser.hasPermission("analytics")) {
204
add(userAnalyticsModule)
205
}
206
207
// Locale-specific modules
208
add(createLocaleModule(currentUser.locale))
209
}
210
}
211
}
212
}
213
```
214
215
## Module Creation Helpers
216
217
### Dynamic Module Creation
218
219
```kotlin
220
fun createUserModule(userId: String) = module {
221
single<UserRepository> { UserRepositoryImpl(userId) }
222
single<UserPreferences> { UserPreferencesImpl(userId) }
223
single<UserCache> { UserCacheImpl(userId) }
224
}
225
226
fun createFeatureModule(featureConfig: FeatureConfig) = module {
227
single { featureConfig }
228
single<FeatureService> {
229
if (featureConfig.useV2) {
230
FeatureServiceV2Impl(get())
231
} else {
232
FeatureServiceV1Impl(get())
233
}
234
}
235
}
236
237
fun createEnvironmentModule(env: String) = module {
238
single<ApiClient> {
239
when (env) {
240
"dev" -> DevApiClient()
241
"staging" -> StagingApiClient()
242
"prod" -> ProdApiClient()
243
else -> throw IllegalArgumentException("Unknown environment: $env")
244
}
245
}
246
}
247
```
248
249
### Module Composition
250
251
```kotlin
252
@Composable
253
fun CompositeModules(config: AppConfig) {
254
rememberKoinModules(unloadModules = true) {
255
buildList {
256
// Core modules always included
257
addAll(coreModules)
258
259
// Add platform-specific modules
260
addAll(getPlatformModules())
261
262
// Add feature modules based on config
263
if (config.enableAnalytics) {
264
add(analyticsModule)
265
}
266
267
if (config.enableCrashReporting) {
268
add(crashReportingModule)
269
}
270
271
// Add theme modules
272
add(getThemeModule(config.theme))
273
}
274
}
275
}
276
277
private fun coreModules() = listOf(
278
networkModule,
279
databaseModule,
280
repositoryModule
281
)
282
283
private fun getPlatformModules() = when (Platform.current) {
284
Platform.Android -> listOf(androidModule)
285
Platform.iOS -> listOf(iosModule)
286
Platform.Desktop -> listOf(desktopModule)
287
}
288
```
289
290
## Testing with Module Management
291
292
### Test Module Injection
293
294
```kotlin
295
@Composable
296
fun TestableComponent(isTestMode: Boolean = false) {
297
if (isTestMode) {
298
rememberKoinModules(unloadModules = true) {
299
listOf(
300
testModule,
301
mockModule
302
)
303
}
304
} else {
305
rememberKoinModules(unloadModules = true) {
306
listOf(
307
productionModule,
308
realModule
309
)
310
}
311
}
312
313
ComponentContent()
314
}
315
316
// Test modules
317
val testModule = module {
318
single<ApiService> { MockApiService() }
319
single<Database> { InMemoryDatabase() }
320
}
321
322
val mockModule = module {
323
single<UserRepository> { MockUserRepository() }
324
single<AnalyticsService> { NoOpAnalyticsService() }
325
}
326
```
327
328
### Preview Module Support
329
330
```kotlin
331
@Preview
332
@Composable
333
fun ComponentPreview() {
334
rememberKoinModules {
335
listOf(previewModule)
336
}
337
338
MyComponent()
339
}
340
341
val previewModule = module {
342
single<DataService> { PreviewDataService() }
343
single<ImageLoader> { MockImageLoader() }
344
}
345
```
346
347
## Performance Considerations
348
349
### Module Loading Performance
350
351
```kotlin
352
@Composable
353
fun OptimizedModuleLoading(heavyFeatureEnabled: Boolean) {
354
// Use remember to avoid recreating modules on recomposition
355
val dynamicModules = remember(heavyFeatureEnabled) {
356
if (heavyFeatureEnabled) {
357
listOf(heavyFeatureModule, additionalModule)
358
} else {
359
emptyList()
360
}
361
}
362
363
rememberKoinModules(unloadModules = true) {
364
dynamicModules
365
}
366
}
367
```
368
369
### Lazy Module Creation
370
371
```kotlin
372
@Composable
373
fun LazyModuleLoading() {
374
// Lazy creation of expensive modules
375
rememberKoinModules(unloadModules = true) {
376
listOf(
377
// Only create when accessed
378
module {
379
single<HeavyService> { HeavyServiceImpl() }
380
}
381
)
382
}
383
}
384
```
385
386
## Internal Implementation
387
388
### CompositionKoinModuleLoader
389
390
The internal class that manages module lifecycle:
391
392
```kotlin { .api }
393
@KoinExperimentalAPI
394
@KoinInternalApi
395
class CompositionKoinModuleLoader(
396
val modules: List<Module>,
397
val koin: Koin,
398
val unloadOnForgotten: Boolean,
399
val unloadOnAbandoned: Boolean
400
) : RememberObserver {
401
402
init {
403
koin.loadModules(modules)
404
}
405
406
override fun onRemembered() {
407
// Module loading happens in init
408
}
409
410
override fun onForgotten() {
411
if (unloadOnForgotten) {
412
unloadModules()
413
}
414
}
415
416
override fun onAbandoned() {
417
if (unloadOnAbandoned) {
418
unloadModules()
419
}
420
}
421
422
private fun unloadModules() {
423
koin.unloadModules(modules)
424
}
425
}
426
```
427
428
## Core Types
429
430
```kotlin { .api }
431
// Module types
432
interface Module {
433
val id: String
434
val createdAtStart: Boolean
435
}
436
437
// Module lifecycle
438
interface RememberObserver {
439
fun onRemembered()
440
fun onForgotten()
441
fun onAbandoned()
442
}
443
444
// Koin instance for module management
445
interface Koin {
446
fun loadModules(modules: List<Module>)
447
fun unloadModules(modules: List<Module>)
448
}
449
```
450
451
## Error Handling
452
453
### Module Loading Failures
454
455
```kotlin
456
@Composable
457
fun SafeModuleLoading() {
458
try {
459
rememberKoinModules(unloadModules = true) {
460
listOf(
461
riskyModule,
462
dependentModule
463
)
464
}
465
466
ModuleContent()
467
} catch (e: ModuleCreationException) {
468
Text("Failed to load modules: ${e.message}")
469
}
470
}
471
```
472
473
### Circular Dependencies
474
475
```kotlin
476
// Avoid circular dependencies in dynamic modules
477
val moduleA = module {
478
single<ServiceA> { ServiceAImpl(get<ServiceB>()) }
479
}
480
481
val moduleB = module {
482
single<ServiceB> { ServiceBImpl(get<ServiceA>()) } // ❌ Circular dependency
483
}
484
485
// Better approach: Use interfaces or break the cycle
486
val moduleA = module {
487
single<ServiceA> { ServiceAImpl(get<ServiceBInterface>()) }
488
}
489
490
val moduleB = module {
491
single<ServiceBInterface> { ServiceBImpl() }
492
}
493
```
494
495
## Best Practices
496
497
### Module Organization
498
499
```kotlin
500
// ✅ Good: Organize modules by feature/layer
501
val networkModule = module { /* network dependencies */ }
502
val dataModule = module { /* data layer dependencies */ }
503
val userFeatureModule = module { /* user feature dependencies */ }
504
505
// ❌ Poor: Monolithic modules
506
val everythingModule = module {
507
// Too many unrelated dependencies
508
}
509
```
510
511
### Lifecycle Management
512
513
```kotlin
514
// ✅ Good: Match module lifecycle to business logic
515
@Composable
516
fun UserSession() {
517
// Load user modules for entire session
518
rememberKoinModules(unloadOnForgotten = true) {
519
listOf(userSessionModule)
520
}
521
}
522
523
// ✅ Good: Temporary modules for short-lived features
524
@Composable
525
fun OnboardingFlow() {
526
rememberKoinModules(unloadModules = true) {
527
listOf(onboardingModule) // Unload when onboarding ends
528
}
529
}
530
```