or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

application-setup.mdcontext-access.mddependency-injection.mdindex.mdmodule-management.mdscope-management.md

scope-management.mddocs/

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

```