or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

collections.mdcomposition.mdeffects.mdindex.mdstate-management.md

effects.mddocs/

0

# Effects and Lifecycle

1

2

Side effect APIs for integrating with external systems, managing resources, and handling component lifecycle in Compose. Effects allow you to perform operations that go beyond the pure functional nature of composables.

3

4

## Capabilities

5

6

### Side Effects

7

8

Execute code that runs on every recomposition.

9

10

```kotlin { .api }

11

/**

12

* Executes a side effect on every recomposition

13

* Use sparingly as it runs on every recomposition

14

* @param effect The side effect to execute

15

*/

16

@Composable

17

fun SideEffect(effect: () -> Unit)

18

```

19

20

**Usage Examples:**

21

22

```kotlin

23

import androidx.compose.runtime.*

24

import androidx.compose.runtime.produceState

25

import androidx.compose.runtime.rememberCoroutineScope

26

27

@Composable

28

fun SideEffectExample(analytics: Analytics) {

29

val screenName = "UserProfile"

30

31

// Runs on every recomposition

32

SideEffect {

33

analytics.trackScreenView(screenName)

34

}

35

36

Text("User Profile Screen")

37

}

38

39

@Composable

40

fun LoggingExample() {

41

var count by remember { mutableStateOf(0) }

42

43

// Logs every time count changes and composable recomposes

44

SideEffect {

45

println("Recomposed with count: $count")

46

}

47

48

Button(onClick = { count++ }) {

49

Text("Count: $count")

50

}

51

}

52

```

53

54

### Launched Effects

55

56

Launch coroutines that are tied to the composable's lifecycle.

57

58

```kotlin { .api }

59

/**

60

* Launches a coroutine in the composition scope

61

* Coroutine is cancelled when the composable leaves composition or keys change

62

* @param keys Dependency keys - effect restarts when any key changes

63

* @param block Suspending block to execute

64

*/

65

@Composable

66

fun LaunchedEffect(

67

vararg keys: Any?,

68

block: suspend CoroutineScope.() -> Unit

69

)

70

```

71

72

**Usage Examples:**

73

74

```kotlin

75

@Composable

76

fun LaunchedEffectExample(userId: String) {

77

var userData by remember { mutableStateOf<UserData?>(null) }

78

79

// Launch effect that restarts when userId changes

80

LaunchedEffect(userId) {

81

userData = null // Show loading

82

try {

83

userData = userRepository.getUser(userId)

84

} catch (e: Exception) {

85

userData = UserData.Error(e.message)

86

}

87

}

88

89

when (val data = userData) {

90

null -> CircularProgressIndicator()

91

is UserData.Success -> UserProfile(data.user)

92

is UserData.Error -> ErrorMessage(data.message)

93

}

94

}

95

96

@Composable

97

fun AnimationEffect() {

98

var animationState by remember { mutableStateOf(0f) }

99

100

// Animate continuously

101

LaunchedEffect(Unit) {

102

while (true) {

103

animationState = (animationState + 0.01f) % 1f

104

delay(16) // ~60 FPS

105

}

106

}

107

108

CustomAnimatedComponent(progress = animationState)

109

}

110

```

111

112

### Disposable Effects

113

114

Effects with cleanup capabilities when the composable leaves composition or keys change.

115

116

```kotlin { .api }

117

/**

118

* Effect that requires cleanup when keys change or component leaves composition

119

* @param keys Dependency keys - effect restarts when any key changes

120

* @param effect Effect block that returns a DisposableEffectResult

121

*/

122

@Composable

123

fun DisposableEffect(

124

vararg keys: Any?,

125

effect: DisposableEffectScope.() -> DisposableEffectResult

126

)

127

128

/**

129

* Scope for disposable effects

130

*/

131

interface DisposableEffectScope {

132

fun onDispose(onDisposeEffect: () -> Unit): DisposableEffectResult

133

}

134

135

/**

136

* Result of a disposable effect providing cleanup callback

137

*/

138

interface DisposableEffectResult

139

```

140

141

**Usage Examples:**

142

143

```kotlin

144

@Composable

145

fun DisposableEffectExample() {

146

val lifecycleOwner = LocalLifecycleOwner.current

147

148

DisposableEffect(lifecycleOwner) {

149

val observer = LifecycleEventObserver { _, event ->

150

when (event) {

151

Lifecycle.Event.ON_START -> println("Component started")

152

Lifecycle.Event.ON_STOP -> println("Component stopped")

153

else -> {}

154

}

155

}

156

157

lifecycleOwner.lifecycle.addObserver(observer)

158

159

onDispose {

160

lifecycleOwner.lifecycle.removeObserver(observer)

161

}

162

}

163

}

164

165

@Composable

166

fun KeyboardListenerExample() {

167

DisposableEffect(Unit) {

168

val keyListener = object : KeyListener {

169

override fun keyPressed(e: KeyEvent) {

170

println("Key pressed: ${e.keyCode}")

171

}

172

}

173

174

KeyboardFocusManager.getCurrentKeyboardFocusManager()

175

.addKeyEventDispatcher(keyListener)

176

177

onDispose {

178

KeyboardFocusManager.getCurrentKeyboardFocusManager()

179

.removeKeyEventDispatcher(keyListener)

180

}

181

}

182

}

183

```

184

185

### Coroutine Scope Management

186

187

Get a coroutine scope that survives recompositions for launching coroutines from event handlers.

188

189

```kotlin { .api }

190

/**

191

* Returns a CoroutineScope that is cancelled when the composable leaves composition

192

* Use this for launching coroutines from event handlers like onClick

193

* @return CoroutineScope tied to the composable's lifecycle

194

*/

195

@Composable

196

fun rememberCoroutineScope(): CoroutineScope

197

```

198

199

**Usage Examples:**

200

201

```kotlin

202

@Composable

203

fun CoroutineScopeExample() {

204

val scope = rememberCoroutineScope()

205

var result by remember { mutableStateOf("") }

206

207

Button(

208

onClick = {

209

// Launch coroutine from event handler

210

scope.launch {

211

result = "Loading..."

212

delay(2000)

213

result = apiService.fetchData()

214

}

215

}

216

) {

217

Text("Fetch Data")

218

}

219

220

Text("Result: $result")

221

}

222

223

@Composable

224

fun ScrollToTopExample() {

225

val listState = rememberLazyListState()

226

val scope = rememberCoroutineScope()

227

228

Column {

229

Button(

230

onClick = {

231

scope.launch {

232

listState.animateScrollToItem(0)

233

}

234

}

235

) {

236

Text("Scroll to Top")

237

}

238

239

LazyColumn(state = listState) {

240

items(100) { index ->

241

Text("Item $index")

242

}

243

}

244

}

245

}

246

```

247

248

### State Production from Async Sources

249

250

Create state from asynchronous data sources.

251

252

```kotlin { .api }

253

/**

254

* Produces state from a suspending producer function

255

* @param initialValue Initial value while producer is running

256

* @param keys Dependency keys - producer restarts when any key changes

257

* @param producer Suspending function that produces values

258

* @return State holding the produced value

259

*/

260

@Composable

261

fun <T> produceState(

262

initialValue: T,

263

vararg keys: Any?,

264

producer: suspend ProduceStateScope<T>.() -> Unit

265

): State<T>

266

267

/**

268

* Scope for producing state values

269

*/

270

interface ProduceStateScope<T> : MutableState<T>, CoroutineScope {

271

suspend fun awaitDispose(onDispose: () -> Unit)

272

}

273

```

274

275

**Usage Examples:**

276

277

```kotlin

278

@Composable

279

fun ProduceStateExample(url: String) {

280

val imageState by produceState<ImageBitmap?>(null, url) {

281

value = loadImageFromNetwork(url)

282

}

283

284

when (val image = imageState) {

285

null -> CircularProgressIndicator()

286

else -> Image(bitmap = image, contentDescription = null)

287

}

288

}

289

290

@Composable

291

fun NetworkDataExample(endpoint: String) {

292

val networkData by produceState(

293

initialValue = NetworkState.Loading,

294

key1 = endpoint

295

) {

296

try {

297

val data = httpClient.get(endpoint)

298

value = NetworkState.Success(data)

299

} catch (e: Exception) {

300

value = NetworkState.Error(e.message ?: "Unknown error")

301

}

302

303

awaitDispose {

304

// Cleanup network resources

305

httpClient.cancel()

306

}

307

}

308

309

when (networkData) {

310

is NetworkState.Loading -> LoadingIndicator()

311

is NetworkState.Success -> DataDisplay(networkData.data)

312

is NetworkState.Error -> ErrorMessage(networkData.message)

313

}

314

}

315

```

316

317

### Flow Integration

318

319

Convert Flow/StateFlow to Compose State.

320

321

```kotlin { .api }

322

/**

323

* Collects values from a Flow and represents them as State

324

* @param initial Initial value to use before first emission

325

* @param context CoroutineContext for collection (default: EmptyCoroutineContext)

326

* @return State that updates with Flow emissions

327

*/

328

@Composable

329

fun <T> Flow<T>.collectAsState(

330

initial: T,

331

context: CoroutineContext = EmptyCoroutineContext

332

): State<T>

333

334

/**

335

* Collects values from a StateFlow and represents them as State

336

* Uses the StateFlow's current value as initial value

337

*/

338

@Composable

339

fun <T> StateFlow<T>.collectAsState(

340

context: CoroutineContext = EmptyCoroutineContext

341

): State<T>

342

```

343

344

**Usage Examples:**

345

346

```kotlin

347

@Composable

348

fun FlowExample(viewModel: MyViewModel) {

349

// Collect from Flow

350

val uiState by viewModel.uiStateFlow.collectAsState()

351

352

// Collect from Flow with initial value

353

val searchResults by viewModel.searchFlow.collectAsState(

354

initial = emptyList()

355

)

356

357

// Collect with custom context

358

val backgroundData by viewModel.backgroundFlow.collectAsState(

359

initial = null,

360

context = Dispatchers.IO

361

)

362

363

when (uiState) {

364

is UiState.Loading -> LoadingScreen()

365

is UiState.Success -> SuccessScreen(uiState.data)

366

is UiState.Error -> ErrorScreen(uiState.message)

367

}

368

}

369

370

@Composable

371

fun DatabaseExample(database: UserDatabase) {

372

val users by database.getAllUsers().collectAsState(initial = emptyList())

373

374

LazyColumn {

375

items(users) { user ->

376

UserItem(user = user)

377

}

378

}

379

}

380

```

381

382

## Advanced Effect Patterns

383

384

### Effect Cleanup Patterns

385

386

Common patterns for cleaning up resources in effects.

387

388

```kotlin

389

@Composable

390

fun ResourceManagementExample() {

391

DisposableEffect(Unit) {

392

val resource = acquireResource()

393

394

onDispose {

395

resource.release()

396

}

397

}

398

399

LaunchedEffect(Unit) {

400

val connection = openConnection()

401

try {

402

// Use connection

403

handleConnection(connection)

404

} finally {

405

connection.close()

406

}

407

}

408

}

409

```

410

411

### Conditional Effects

412

413

Effects that run conditionally based on state.

414

415

```kotlin

416

@Composable

417

fun ConditionalEffectExample() {

418

var isActive by remember { mutableStateOf(false) }

419

420

if (isActive) {

421

LaunchedEffect(Unit) {

422

startPeriodicUpdate()

423

}

424

}

425

426

// Alternative pattern

427

LaunchedEffect(isActive) {

428

if (isActive) {

429

startService()

430

}

431

}

432

}

433

```