Tree composition support and runtime infrastructure for Compose Multiplatform desktop applications with declarative UI development APIs.
—
Reactive collections and observable data structures that integrate with the Compose runtime system. These collections automatically trigger recomposition when their contents change.
Mutable lists that trigger recomposition when modified.
/**
* Creates a mutable list that triggers recomposition when changed
* @param elements Initial elements for the list
* @return SnapshotStateList instance
*/
fun <T> mutableStateListOf(vararg elements: T): SnapshotStateList<T>
/**
* Mutable list implementation that integrates with Compose state system
* Modifications to this list will trigger recomposition of observing composables
*/
interface SnapshotStateList<T> : MutableList<T> {
/**
* Adds all elements from the specified collection
* @param elements Collection of elements to add
* @return true if list was modified
*/
fun addAll(elements: Collection<T>): Boolean
/**
* Removes all elements that match the predicate
* @param predicate Function to test elements
* @return true if any elements were removed
*/
fun removeAll(predicate: (T) -> Boolean): Boolean
}Usage Examples:
import androidx.compose.runtime.*
@Composable
fun StateListExample() {
val items = remember { mutableStateListOf("Apple", "Banana", "Cherry") }
Column {
// Display list items - recomposes when list changes
items.forEach { item ->
Text("• $item")
}
Row {
Button(onClick = { items.add("Orange") }) {
Text("Add Orange")
}
Button(onClick = { items.removeAt(0) }) {
Text("Remove First")
}
}
}
}
@Composable
fun TodoListExample() {
val todos = remember { mutableStateListOf<Todo>() }
var newTodoText by remember { mutableStateOf("") }
Column {
LazyColumn {
items(todos) { todo ->
TodoItem(
todo = todo,
onToggle = {
val index = todos.indexOf(todo)
todos[index] = todo.copy(completed = !todo.completed)
},
onDelete = { todos.remove(todo) }
)
}
}
Row {
TextField(
value = newTodoText,
onValueChange = { newTodoText = it }
)
Button(
onClick = {
if (newTodoText.isNotEmpty()) {
todos.add(Todo(text = newTodoText, completed = false))
newTodoText = ""
}
}
) {
Text("Add")
}
}
}
}Mutable maps that trigger recomposition when modified.
/**
* Creates a mutable map that triggers recomposition when changed
* @param pairs Initial key-value pairs for the map
* @return SnapshotStateMap instance
*/
fun <K, V> mutableStateMapOf(vararg pairs: Pair<K, V>): SnapshotStateMap<K, V>
/**
* Mutable map implementation that integrates with Compose state system
* Modifications to this map will trigger recomposition of observing composables
*/
interface SnapshotStateMap<K, V> : MutableMap<K, V> {
/**
* Puts all key-value pairs from the specified map
* @param from Map containing pairs to add
*/
fun putAll(from: Map<out K, V>)
/**
* Removes entries that match the predicate
* @param predicate Function to test entries
* @return true if any entries were removed
*/
fun removeAll(predicate: (Map.Entry<K, V>) -> Boolean): Boolean
}Usage Examples:
@Composable
fun StateMapExample() {
val userScores = remember {
mutableStateMapOf(
"Alice" to 100,
"Bob" to 85,
"Charlie" to 92
)
}
Column {
Text("Leaderboard")
// Display scores - recomposes when map changes
userScores.entries.sortedByDescending { it.value }.forEach { (name, score) ->
Text("$name: $score points")
}
Row {
Button(onClick = { userScores["Alice"] = (userScores["Alice"] ?: 0) + 10 }) {
Text("Boost Alice")
}
Button(onClick = { userScores["David"] = 75 }) {
Text("Add David")
}
}
Text("Total players: ${userScores.size}")
}
}
@Composable
fun ConfigurationExample() {
val settings = remember {
mutableStateMapOf(
"darkMode" to false,
"notifications" to true,
"autoSave" to true
)
}
Column {
settings.entries.forEach { (key, value) ->
Row {
Switch(
checked = value as Boolean,
onCheckedChange = { settings[key] = it }
)
Text(key)
}
}
Button(
onClick = {
settings.clear()
// Reset to defaults
settings.putAll(mapOf(
"darkMode" to false,
"notifications" to true,
"autoSave" to true
))
}
) {
Text("Reset Settings")
}
}
}Convert reactive streams to Compose State.
/**
* Collects values from a Flow and represents them as State
* @param initial Initial value to use before first emission
* @param context CoroutineContext for collection (default: EmptyCoroutineContext)
* @return State that updates with Flow emissions
*/
@Composable
fun <T> Flow<T>.collectAsState(
initial: T,
context: CoroutineContext = EmptyCoroutineContext
): State<T>
/**
* Collects values from a StateFlow and represents them as State
* Uses the StateFlow's current value as initial value
* @param context CoroutineContext for collection (default: EmptyCoroutineContext)
* @return State that updates with StateFlow emissions
*/
@Composable
fun <T> StateFlow<T>.collectAsState(
context: CoroutineContext = EmptyCoroutineContext
): State<T>Usage Examples:
@Composable
fun FlowCollectionExample(repository: DataRepository) {
// Collect list data from Flow
val items by repository.getItemsFlow().collectAsState(
initial = emptyList()
)
// Collect search results
val searchQuery by repository.searchQueryFlow().collectAsState(
initial = ""
)
val filteredItems = remember(items, searchQuery) {
if (searchQuery.isEmpty()) items else items.filter {
it.name.contains(searchQuery, ignoreCase = true)
}
}
LazyColumn {
items(filteredItems) { item ->
ItemRow(item = item)
}
}
}
@Composable
fun ViewModelExample(viewModel: MyViewModel) {
// Collect UI state from ViewModel
val uiState by viewModel.uiState.collectAsState()
// Collect loading state
val isLoading by viewModel.isLoading.collectAsState()
when (uiState) {
is UiState.Loading -> LoadingIndicator()
is UiState.Success -> {
SuccessContent(
data = uiState.data,
isRefreshing = isLoading
)
}
is UiState.Error -> ErrorMessage(uiState.message)
}
}Useful extensions for working with state lists.
/**
* Converts a regular List to a SnapshotStateList
*/
fun <T> List<T>.toMutableStateList(): SnapshotStateList<T>
/**
* Creates a state list from a collection
*/
fun <T> Collection<T>.toMutableStateList(): SnapshotStateList<T>Usage Examples:
@Composable
fun ListConversionExample() {
// Convert existing list to state list
val originalList = listOf("A", "B", "C")
val stateList = remember { originalList.toMutableStateList() }
// Now mutations will trigger recomposition
Button(onClick = { stateList.add("D") }) {
Text("Add D")
}
}
@Composable
fun DataLoadingExample(initialData: List<String>) {
val items = remember(initialData) {
initialData.toMutableStateList()
}
LaunchedEffect(Unit) {
val newItems = loadAdditionalData()
items.addAll(newItems)
}
LazyColumn {
items(items) { item ->
Text(item)
}
}
}Advanced usage of the snapshot system underlying state collections.
/**
* Takes a snapshot of the current state
* @return Snapshot instance
*/
fun Snapshot.Companion.take(): Snapshot
/**
* Creates a mutable snapshot for making isolated changes
*/
fun Snapshot.Companion.takeMutableSnapshot(
readObserver: ((Any) -> Unit)? = null,
writeObserver: ((Any) -> Unit)? = null
): MutableSnapshot
/**
* Base class for snapshots of the state system
*/
abstract class Snapshot {
companion object
/**
* Applies changes made in this snapshot
* @return SnapshotApplyResult indicating success or conflicts
*/
abstract fun apply(): SnapshotApplyResult
/**
* Disposes of this snapshot, releasing resources
*/
abstract fun dispose()
}
/**
* A mutable snapshot that allows making changes within an isolated scope
*/
abstract class MutableSnapshot : Snapshot {
/**
* Enters the snapshot scope for making changes
*/
abstract fun <T> enter(block: () -> T): T
}
/**
* Result of applying a snapshot
*/
sealed class SnapshotApplyResult {
object Success : SnapshotApplyResult()
data class Failure(val snapshot: Snapshot) : SnapshotApplyResult()
}Usage Examples:
@Composable
fun SnapshotExample() {
val items = remember { mutableStateListOf("A", "B", "C") }
Button(
onClick = {
// Make changes in an isolated snapshot
val snapshot = Snapshot.takeMutableSnapshot()
snapshot.enter {
items.add("D")
items.add("E")
// Changes aren't visible yet
}
// Apply changes atomically
snapshot.apply()
snapshot.dispose()
}
) {
Text("Add Multiple Items")
}
}Create derived collections that update automatically.
@Composable
fun FilteredCollectionExample() {
val allItems = remember { mutableStateListOf<Item>() }
var filterText by remember { mutableStateOf("") }
val filteredItems = remember(allItems, filterText) {
derivedStateOf {
if (filterText.isEmpty()) {
allItems.toList()
} else {
allItems.filter { it.name.contains(filterText, ignoreCase = true) }
}
}
}.value
Column {
TextField(
value = filterText,
onValueChange = { filterText = it },
label = { Text("Filter") }
)
LazyColumn {
items(filteredItems) { item ->
ItemCard(item = item)
}
}
}
}Group collections by key with automatic updates.
@Composable
fun GroupedCollectionExample() {
val items = remember { mutableStateListOf<Task>() }
val groupedItems = remember(items) {
derivedStateOf {
items.groupBy { it.category }
}
}.value
LazyColumn {
groupedItems.forEach { (category, tasks) ->
item {
Text(
text = category,
style = MaterialTheme.typography.h6
)
}
items(tasks) { task ->
TaskItem(task = task)
}
}
}
}Maintain sorted collections that update automatically.
@Composable
fun SortedCollectionExample() {
val items = remember { mutableStateListOf<Product>() }
var sortBy by remember { mutableStateOf(SortCriteria.NAME) }
val sortedItems = remember(items, sortBy) {
derivedStateOf {
when (sortBy) {
SortCriteria.NAME -> items.sortedBy { it.name }
SortCriteria.PRICE -> items.sortedBy { it.price }
SortCriteria.RATING -> items.sortedByDescending { it.rating }
}
}
}.value
Column {
SortingOptions(
currentSort = sortBy,
onSortChange = { sortBy = it }
)
LazyColumn {
items(sortedItems) { product ->
ProductCard(product = product)
}
}
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-jetbrains-compose-runtime--runtime-desktop