CPS251 Android Development by Scott Shaper

Understanding State in Compose

Think of state in Compose like a memory box that remembers things for you. Just like how you might remember your score in a game or what level you're on, state helps your app remember information that can change over time. We'll learn how to use state to make your apps interactive and responsive to user actions.

Quick Reference

Command/Topic Description Common Use
remember Ensures a value, like a state object, survives across UI updates (recompositions) for a specific Composable. Without it, the value would be re-initialized every time the UI redraws. Storing data that needs to persist during a Composable's active display
mutableStateOf Creates an observable state holder. When its value changes, Compose automatically triggers a recomposition of any UI elements that are reading this state, making your app interactive. Making UI elements interactive and triggering automatic UI updates
by A Kotlin property delegate used with remember { mutableStateOf(...) }. It lets you read and write the state value directly (e.g. count, count++) instead of using .value every time (e.g. count.value, count.value = 5). Simplifying state variable declarations for cleaner code

When to Use State

Practical Examples

Basic Counter

This example shows how to create a simple counter that increases when clicked. Think of it like a scoreboard that updates automatically.

@Composable
fun Counter() {
    // This creates a state variable that starts at 0
    var count by remember { mutableStateOf(0) }

    Column {
        // Display the current count
        Text("Count: $count")
        
        // A button that increases the count when clicked
        Button(onClick = { count++ }) {
            Text("Add One")
        }
    }
}

What This Example Is Doing

Counter creates count with remember { mutableStateOf(0) } so the value survives recomposition and triggers UI updates when it changes. The column shows "Count: 0" (or the current count) and an "Add One" button. Clicking the button runs count++; Compose recomposes and the Text shows the new number. So one state variable drives both the display and the update.

Common State Patterns

Here are some common ways to use state in your apps:

Text Input
@Composable
fun TextInputExample() {
    var text by remember { mutableStateOf("") }
    
    TextField(
        value = text,
        onValueChange = { text = it } /*it refers to the parameter passed to the lambda function. In this case, it represents the new value of the text input field when the user types or changes the text.*/
    )
}

What This Example Is Doing

TextInputExample keeps text in state (initially empty). The TextField’s value is text and onValueChange = { text = it } updates that state when the user types. So it is the new string from the field; assigning it to text updates state and recomposition shows the new value in the field. The UI and state stay in sync.

Checkboxes
@Composable
fun CheckboxExample() {
    var isChecked by remember { mutableStateOf(false) }
    
    Row {
        Checkbox(
            checked = isChecked,
            onCheckedChange = { isChecked = it } //it refers to either true or false
        )
        Text("Check me!")
    }
}

What This Example Is Doing

CheckboxExample keeps isChecked in state (initially false). The Checkbox’s checked is bound to isChecked and onCheckedChange = { isChecked = it } updates it when the user taps the checkbox. So it is the new boolean (true or false). The row shows the checkbox and "Check me!"; the checkbox stays checked or unchecked because state is remembered across recompositions.

Lists
@Composable
fun ListExample() {
    var items by remember { mutableStateOf(listOf("Item 1", "Item 2")) }
    
    Column {
        for (item in items) {
            Text(item) //will create two Text composables, one for each item in the list
        }
    }
}

What This Example Is Doing

ListExample keeps items in state—a list of strings ("Item 1", "Item 2"). The column loops over items and creates a Text for each. So you see two lines. If you later did items = items + "Item 3" (or similar) in response to a button or input, the state would change and the column would recompose to show three items. State can hold any type, including lists.

Interactive Counter App

This example demonstrates a more complete counter with both increase and decrease buttons, showing how state can be modified in different ways.

@Composable
fun CounterApp() {
    // Remember the current count
    var count by remember { mutableStateOf(0) }

    Column(
        modifier = Modifier.padding(16.dp),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        // Show the current count
        Text(
            text = "Count: $count",
            style = TextStyle(fontSize = 24.sp)
        )
        
        // Add some space between elements
        Spacer(modifier = Modifier.height(16.dp))
        
        // Row of buttons to control the count
        Row {
            // Button to decrease count
            Button(onClick = { count-- }) {
                Text("-")
            }
            
            // Add space between buttons
            Spacer(modifier = Modifier.width(8.dp))
            
            // Button to increase count
            Button(onClick = { count++ }) {
                Text("+")
            }
        }
    }
}

What This Example Is Doing

CounterApp keeps count in state and shows it with large text. A row has two buttons: "−" runs count-- and "+" runs count++. So the user can increase or decrease the count; the displayed number updates each time because Compose recomposes when count changes. The remembered state is the count value.

How these examples render

The image below shows the counter app (e.g. after tapping the + button a few times—the count is the remembered state). The snippets above are only part of the code; to see and run the full project, go to my GitHub page and open the chapter6 state.kt file.

State Example

Learning Aids

Tips for Success

Common Mistakes to Avoid

Best Practices

To try these examples, go to my GitHub page and look at the chapter6 state.kt file.