This document provides detailed information about the state management APIs in the Summon library.
Summon provides a hierarchy of state interfaces for different use cases.
The base interface for read-only state holders.
interface State<T> {
val value: T
}
Interface for mutable state holders.
interface SummonMutableState<T> : State<T> {
override var value: T
}
Extended mutable state interface with component functions and listeners.
interface MutableState<T> : SummonMutableState<T> {
operator fun component1(): T
operator fun component2(): (T) -> Unit
fun addListener(listener: (T) -> Unit)
fun removeListener(listener: (T) -> Unit)
operator fun getValue(thisRef: Any?, property: KProperty<*>): T
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T)
}
Enable convenient property delegation syntax with state objects.
// For read-only state
operator fun <T> State<T>.getValue(thisRef: Any?, property: KProperty<*>): T
// For mutable state
operator fun <T> SummonMutableState<T>.setValue(thisRef: Any?, property: KProperty<*>, value: T)
The primary implementation for mutable state management in Summon.
fun <T> mutableStateOf(initialValue: T): MutableState<T>
Creates a new MutableState instance with the given initial value.
@Composable
fun CounterExample() {
// Direct value access
val state = mutableStateOf(0)
Text("Count: ${state.value}")
Button(
text = "Increment",
onClick = { state.value++ }
)
// Property delegation
var count by mutableStateOf(0)
Text("Count: $count")
Button(
text = "Increment",
onClick = { count++ }
)
// Destructuring
val (count2, setCount) = mutableStateOf(0)
Text("Count: $count2")
Button(
text = "Increment",
onClick = { setCount(count2 + 1) }
)
}
MutableState supports adding listeners for value changes:
val state = mutableStateOf("initial")
// Add listener
state.addListener { newValue ->
println("State changed to: $newValue")
}
// Remove listener
val listener: (String) -> Unit = { println("Listener: $it") }
state.addListener(listener)
state.removeListener(listener)
The remember functions preserve state and values across recompositions.
@Composable
fun <T> remember(calculation: () -> T): T
Remembers a value across recompositions. The calculation is only executed once.
@Composable
fun <T> remember(vararg keys: Any?, calculation: () -> T): T
Remembers a value and recalculates when any of the keys change.
@Composable
fun <T> rememberMutableStateOf(initial: T): SummonMutableState<T>
Creates and remembers a mutable state with the given initial value.
@Composable
fun RememberExample(items: List<String>, searchQuery: String) {
// Simple remember
val count = remember { mutableStateOf(0) }
// With keys - recalculates when items or searchQuery change
val filteredItems = remember(items, searchQuery) {
items.filter { it.contains(searchQuery, ignoreCase = true) }
}
// Remember mutable state
var localState by rememberMutableStateOf("initial")
Column {
Text("Count: ${count.value}")
Button(
text = "Increment",
onClick = { count.value++ }
)
Text("Filtered Items: ${filteredItems.size}")
TextField(
value = localState,
onValueChange = { localState = it }
)
}
}
Create state that is computed from other state values.
fun <T> simpleDerivedStateOf(calculation: () -> T): State<T>
Creates a simple derived state that recomputes when accessed.
@Composable
fun <T> derivedStateOf(calculation: () -> T): SummonMutableState<T>
@Composable
fun <T> derivedStateOf(vararg dependencies: Any?, calculation: () -> T): SummonMutableState<T>
Creates derived state that automatically updates when dependencies change.
@Composable
fun DerivedStateExample() {
val firstName = remember { mutableStateOf("John") }
val lastName = remember { mutableStateOf("Doe") }
// Simple derived state - recalculates on every access
val simpleFullName = simpleDerivedStateOf {
"${firstName.value} ${lastName.value}"
}
// Composable derived state - updates when dependencies change
val fullName by derivedStateOf {
"${firstName.value} ${lastName.value}"
}
// Derived state with explicit dependencies
val greeting by derivedStateOf(firstName, lastName) {
"Hello, ${firstName.value} ${lastName.value}!"
}
Column {
Text("Simple: ${simpleFullName.value}")
Text("Full Name: $fullName")
Text(greeting)
TextField(
value = firstName.value,
onValueChange = { firstName.value = it },
label = "First Name"
)
TextField(
value = lastName.value,
onValueChange = { lastName.value = it },
label = "Last Name"
)
}
}
Summon provides integration with Kotlin Flows for reactive state management.
@Composable
fun <T> Flow<T>.collectAsState(initial: T): State<T>
@Composable
fun <T> StateFlow<T>.collectAsState(): State<T>
Converts a Flow or StateFlow into a State that updates when the Flow emits new values.
@Composable
fun <T> produceState(
initialValue: T,
vararg keys: Any?,
producer: suspend ProduceStateScope<T>.() -> Unit
): State<T>
interface ProduceStateScope<T> {
var value: T
}
Creates state from a suspend function, perfect for async operations.
@Composable
fun FlowIntegrationExample(userId: String) {
// Collect from StateFlow
val user = userRepository.getUserFlow(userId).collectAsState()
// Collect from Flow with initial value
val notifications = notificationService
.getNotificationsFlow()
.collectAsState(initial = emptyList())
// Produce state from async operation
val weather = produceState<Weather?>(
initialValue = null,
key1 = userId
) {
value = weatherService.getWeather(userId)
// Update every 5 minutes
while (true) {
delay(5.minutes)
try {
value = weatherService.getWeather(userId)
} catch (e: Exception) {
// Handle error
}
}
}
Column {
user.value?.let { u ->
Text("User: ${u.name}")
}
Text("Notifications: ${notifications.value.size}")
weather.value?.let { w ->
Text("Temperature: ${w.temperature}°C")
} ?: Text("Loading weather...")
}
}
Persist state values across recompositions and app restarts.
object SaveableStateRegistry {
fun <T> get(key: String): T?
fun set(key: String, value: Any?)
fun clear()
}
fun <T> rememberSaveable(key: String, initialValue: T): SummonMutableState<T>
fun <T> rememberWithIdentifier(identifier: String, initialValue: T): SummonMutableState<T>
Creates state that persists across recompositions.
fun clearSaveableStates()
fun hasSaveableState(key: String): Boolean
@Composable
fun SaveableStateExample() {
// State persisted with unique key
val settings = rememberSaveable("user_settings", UserSettings())
// State persisted with identifier
var theme by rememberWithIdentifier("app_theme", "light")
// Check if state exists
val hasPersistedData = hasSaveableState("user_data")
Column {
Text("Theme: $theme")
Button(
text = if (theme == "light") "Dark Mode" else "Light Mode",
onClick = {
theme = if (theme == "light") "dark" else "light"
}
)
Text("Has persisted data: $hasPersistedData")
Button(
text = "Clear All Saved States",
onClick = { clearSaveableStates() }
)
}
}
@Composable
fun PersistentCounter() {
var count by rememberSaveable("counter_value", 0)
// Derived state for display
val countDisplay by derivedStateOf {
"Count: $count"
}
// Track if count is even/odd
val isEven by derivedStateOf(count) {
count % 2 == 0
}
Column {
Text(countDisplay)
Text("Is even: $isEven")
Row {
Button(
text = "Increment",
onClick = { count++ }
)
Button(
text = "Decrement",
onClick = { count-- }
)
Button(
text = "Reset",
onClick = { count = 0 }
)
}
}
}
@Composable
fun AsyncDataExample(query: String) {
// Loading state
var isLoading by rememberMutableStateOf(false)
// Error state
var error by rememberMutableStateOf<String?>(null)
// Data state from async operation
val searchResults = produceState<List<SearchResult>>(
initialValue = emptyList(),
key1 = query
) {
if (query.isNotBlank()) {
isLoading = true
error = null
try {
value = searchService.search(query)
} catch (e: Exception) {
error = e.message
value = emptyList()
} finally {
isLoading = false
}
}
}
Column {
when {
isLoading -> Text("Loading...")
error != null -> Text("Error: $error")
searchResults.value.isEmpty() && query.isNotBlank() -> Text("No results")
else -> {
searchResults.value.forEach { result ->
Text(result.title)
}
}
}
}
}
State<T>, SummonMutableState<T>, or MutableState<T> based on your needsproduceState coroutinesremember and derivedStateOf for better control