or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

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

collections.mddocs/

0

# Collections and Observables

1

2

Reactive collections and observable data structures that integrate with the Compose runtime system. These collections automatically trigger recomposition when their contents change.

3

4

## Capabilities

5

6

### State Lists

7

8

Mutable lists that trigger recomposition when modified.

9

10

```kotlin { .api }

11

/**

12

* Creates a mutable list that triggers recomposition when changed

13

* @param elements Initial elements for the list

14

* @return SnapshotStateList instance

15

*/

16

fun <T> mutableStateListOf(vararg elements: T): SnapshotStateList<T>

17

18

/**

19

* Mutable list implementation that integrates with Compose state system

20

* Modifications to this list will trigger recomposition of observing composables

21

*/

22

interface SnapshotStateList<T> : MutableList<T> {

23

/**

24

* Adds all elements from the specified collection

25

* @param elements Collection of elements to add

26

* @return true if list was modified

27

*/

28

fun addAll(elements: Collection<T>): Boolean

29

30

/**

31

* Removes all elements that match the predicate

32

* @param predicate Function to test elements

33

* @return true if any elements were removed

34

*/

35

fun removeAll(predicate: (T) -> Boolean): Boolean

36

}

37

```

38

39

**Usage Examples:**

40

41

```kotlin

42

import androidx.compose.runtime.*

43

44

@Composable

45

fun StateListExample() {

46

val items = remember { mutableStateListOf("Apple", "Banana", "Cherry") }

47

48

Column {

49

// Display list items - recomposes when list changes

50

items.forEach { item ->

51

Text("• $item")

52

}

53

54

Row {

55

Button(onClick = { items.add("Orange") }) {

56

Text("Add Orange")

57

}

58

59

Button(onClick = { items.removeAt(0) }) {

60

Text("Remove First")

61

}

62

}

63

}

64

}

65

66

@Composable

67

fun TodoListExample() {

68

val todos = remember { mutableStateListOf<Todo>() }

69

var newTodoText by remember { mutableStateOf("") }

70

71

Column {

72

LazyColumn {

73

items(todos) { todo ->

74

TodoItem(

75

todo = todo,

76

onToggle = {

77

val index = todos.indexOf(todo)

78

todos[index] = todo.copy(completed = !todo.completed)

79

},

80

onDelete = { todos.remove(todo) }

81

)

82

}

83

}

84

85

Row {

86

TextField(

87

value = newTodoText,

88

onValueChange = { newTodoText = it }

89

)

90

Button(

91

onClick = {

92

if (newTodoText.isNotEmpty()) {

93

todos.add(Todo(text = newTodoText, completed = false))

94

newTodoText = ""

95

}

96

}

97

) {

98

Text("Add")

99

}

100

}

101

}

102

}

103

```

104

105

### State Maps

106

107

Mutable maps that trigger recomposition when modified.

108

109

```kotlin { .api }

110

/**

111

* Creates a mutable map that triggers recomposition when changed

112

* @param pairs Initial key-value pairs for the map

113

* @return SnapshotStateMap instance

114

*/

115

fun <K, V> mutableStateMapOf(vararg pairs: Pair<K, V>): SnapshotStateMap<K, V>

116

117

/**

118

* Mutable map implementation that integrates with Compose state system

119

* Modifications to this map will trigger recomposition of observing composables

120

*/

121

interface SnapshotStateMap<K, V> : MutableMap<K, V> {

122

/**

123

* Puts all key-value pairs from the specified map

124

* @param from Map containing pairs to add

125

*/

126

fun putAll(from: Map<out K, V>)

127

128

/**

129

* Removes entries that match the predicate

130

* @param predicate Function to test entries

131

* @return true if any entries were removed

132

*/

133

fun removeAll(predicate: (Map.Entry<K, V>) -> Boolean): Boolean

134

}

135

```

136

137

**Usage Examples:**

138

139

```kotlin

140

@Composable

141

fun StateMapExample() {

142

val userScores = remember {

143

mutableStateMapOf(

144

"Alice" to 100,

145

"Bob" to 85,

146

"Charlie" to 92

147

)

148

}

149

150

Column {

151

Text("Leaderboard")

152

153

// Display scores - recomposes when map changes

154

userScores.entries.sortedByDescending { it.value }.forEach { (name, score) ->

155

Text("$name: $score points")

156

}

157

158

Row {

159

Button(onClick = { userScores["Alice"] = (userScores["Alice"] ?: 0) + 10 }) {

160

Text("Boost Alice")

161

}

162

163

Button(onClick = { userScores["David"] = 75 }) {

164

Text("Add David")

165

}

166

}

167

168

Text("Total players: ${userScores.size}")

169

}

170

}

171

172

@Composable

173

fun ConfigurationExample() {

174

val settings = remember {

175

mutableStateMapOf(

176

"darkMode" to false,

177

"notifications" to true,

178

"autoSave" to true

179

)

180

}

181

182

Column {

183

settings.entries.forEach { (key, value) ->

184

Row {

185

Switch(

186

checked = value as Boolean,

187

onCheckedChange = { settings[key] = it }

188

)

189

Text(key)

190

}

191

}

192

193

Button(

194

onClick = {

195

settings.clear()

196

// Reset to defaults

197

settings.putAll(mapOf(

198

"darkMode" to false,

199

"notifications" to true,

200

"autoSave" to true

201

))

202

}

203

) {

204

Text("Reset Settings")

205

}

206

}

207

}

208

```

209

210

### Flow Integration

211

212

Convert reactive streams to Compose State.

213

214

```kotlin { .api }

215

/**

216

* Collects values from a Flow and represents them as State

217

* @param initial Initial value to use before first emission

218

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

219

* @return State that updates with Flow emissions

220

*/

221

@Composable

222

fun <T> Flow<T>.collectAsState(

223

initial: T,

224

context: CoroutineContext = EmptyCoroutineContext

225

): State<T>

226

227

/**

228

* Collects values from a StateFlow and represents them as State

229

* Uses the StateFlow's current value as initial value

230

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

231

* @return State that updates with StateFlow emissions

232

*/

233

@Composable

234

fun <T> StateFlow<T>.collectAsState(

235

context: CoroutineContext = EmptyCoroutineContext

236

): State<T>

237

```

238

239

**Usage Examples:**

240

241

```kotlin

242

@Composable

243

fun FlowCollectionExample(repository: DataRepository) {

244

// Collect list data from Flow

245

val items by repository.getItemsFlow().collectAsState(

246

initial = emptyList()

247

)

248

249

// Collect search results

250

val searchQuery by repository.searchQueryFlow().collectAsState(

251

initial = ""

252

)

253

254

val filteredItems = remember(items, searchQuery) {

255

if (searchQuery.isEmpty()) items else items.filter {

256

it.name.contains(searchQuery, ignoreCase = true)

257

}

258

}

259

260

LazyColumn {

261

items(filteredItems) { item ->

262

ItemRow(item = item)

263

}

264

}

265

}

266

267

@Composable

268

fun ViewModelExample(viewModel: MyViewModel) {

269

// Collect UI state from ViewModel

270

val uiState by viewModel.uiState.collectAsState()

271

272

// Collect loading state

273

val isLoading by viewModel.isLoading.collectAsState()

274

275

when (uiState) {

276

is UiState.Loading -> LoadingIndicator()

277

is UiState.Success -> {

278

SuccessContent(

279

data = uiState.data,

280

isRefreshing = isLoading

281

)

282

}

283

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

284

}

285

}

286

```

287

288

### List Extensions

289

290

Useful extensions for working with state lists.

291

292

```kotlin { .api }

293

/**

294

* Converts a regular List to a SnapshotStateList

295

*/

296

fun <T> List<T>.toMutableStateList(): SnapshotStateList<T>

297

298

/**

299

* Creates a state list from a collection

300

*/

301

fun <T> Collection<T>.toMutableStateList(): SnapshotStateList<T>

302

```

303

304

**Usage Examples:**

305

306

```kotlin

307

@Composable

308

fun ListConversionExample() {

309

// Convert existing list to state list

310

val originalList = listOf("A", "B", "C")

311

val stateList = remember { originalList.toMutableStateList() }

312

313

// Now mutations will trigger recomposition

314

Button(onClick = { stateList.add("D") }) {

315

Text("Add D")

316

}

317

}

318

319

@Composable

320

fun DataLoadingExample(initialData: List<String>) {

321

val items = remember(initialData) {

322

initialData.toMutableStateList()

323

}

324

325

LaunchedEffect(Unit) {

326

val newItems = loadAdditionalData()

327

items.addAll(newItems)

328

}

329

330

LazyColumn {

331

items(items) { item ->

332

Text(item)

333

}

334

}

335

}

336

```

337

338

### Snapshot System Integration

339

340

Advanced usage of the snapshot system underlying state collections.

341

342

```kotlin { .api }

343

/**

344

* Takes a snapshot of the current state

345

* @return Snapshot instance

346

*/

347

fun Snapshot.Companion.take(): Snapshot

348

349

/**

350

* Creates a mutable snapshot for making isolated changes

351

*/

352

fun Snapshot.Companion.takeMutableSnapshot(

353

readObserver: ((Any) -> Unit)? = null,

354

writeObserver: ((Any) -> Unit)? = null

355

): MutableSnapshot

356

357

/**

358

* Base class for snapshots of the state system

359

*/

360

abstract class Snapshot {

361

companion object

362

363

/**

364

* Applies changes made in this snapshot

365

* @return SnapshotApplyResult indicating success or conflicts

366

*/

367

abstract fun apply(): SnapshotApplyResult

368

369

/**

370

* Disposes of this snapshot, releasing resources

371

*/

372

abstract fun dispose()

373

}

374

375

/**

376

* A mutable snapshot that allows making changes within an isolated scope

377

*/

378

abstract class MutableSnapshot : Snapshot {

379

/**

380

* Enters the snapshot scope for making changes

381

*/

382

abstract fun <T> enter(block: () -> T): T

383

}

384

385

/**

386

* Result of applying a snapshot

387

*/

388

sealed class SnapshotApplyResult {

389

object Success : SnapshotApplyResult()

390

data class Failure(val snapshot: Snapshot) : SnapshotApplyResult()

391

}

392

```

393

394

**Usage Examples:**

395

396

```kotlin

397

@Composable

398

fun SnapshotExample() {

399

val items = remember { mutableStateListOf("A", "B", "C") }

400

401

Button(

402

onClick = {

403

// Make changes in an isolated snapshot

404

val snapshot = Snapshot.takeMutableSnapshot()

405

snapshot.enter {

406

items.add("D")

407

items.add("E")

408

// Changes aren't visible yet

409

}

410

411

// Apply changes atomically

412

snapshot.apply()

413

snapshot.dispose()

414

}

415

) {

416

Text("Add Multiple Items")

417

}

418

}

419

```

420

421

## Collection Patterns

422

423

### Filtered Collections

424

425

Create derived collections that update automatically.

426

427

```kotlin

428

@Composable

429

fun FilteredCollectionExample() {

430

val allItems = remember { mutableStateListOf<Item>() }

431

var filterText by remember { mutableStateOf("") }

432

433

val filteredItems = remember(allItems, filterText) {

434

derivedStateOf {

435

if (filterText.isEmpty()) {

436

allItems.toList()

437

} else {

438

allItems.filter { it.name.contains(filterText, ignoreCase = true) }

439

}

440

}

441

}.value

442

443

Column {

444

TextField(

445

value = filterText,

446

onValueChange = { filterText = it },

447

label = { Text("Filter") }

448

)

449

450

LazyColumn {

451

items(filteredItems) { item ->

452

ItemCard(item = item)

453

}

454

}

455

}

456

}

457

```

458

459

### Grouped Collections

460

461

Group collections by key with automatic updates.

462

463

```kotlin

464

@Composable

465

fun GroupedCollectionExample() {

466

val items = remember { mutableStateListOf<Task>() }

467

468

val groupedItems = remember(items) {

469

derivedStateOf {

470

items.groupBy { it.category }

471

}

472

}.value

473

474

LazyColumn {

475

groupedItems.forEach { (category, tasks) ->

476

item {

477

Text(

478

text = category,

479

style = MaterialTheme.typography.h6

480

)

481

}

482

483

items(tasks) { task ->

484

TaskItem(task = task)

485

}

486

}

487

}

488

}

489

```

490

491

### Sorted Collections

492

493

Maintain sorted collections that update automatically.

494

495

```kotlin

496

@Composable

497

fun SortedCollectionExample() {

498

val items = remember { mutableStateListOf<Product>() }

499

var sortBy by remember { mutableStateOf(SortCriteria.NAME) }

500

501

val sortedItems = remember(items, sortBy) {

502

derivedStateOf {

503

when (sortBy) {

504

SortCriteria.NAME -> items.sortedBy { it.name }

505

SortCriteria.PRICE -> items.sortedBy { it.price }

506

SortCriteria.RATING -> items.sortedByDescending { it.rating }

507

}

508

}

509

}.value

510

511

Column {

512

SortingOptions(

513

currentSort = sortBy,

514

onSortChange = { sortBy = it }

515

)

516

517

LazyColumn {

518

items(sortedItems) { product ->

519

ProductCard(product = product)

520

}

521

}

522

}

523

}

524

```