Managing state in Jetpack Compose: remember vs derivedStateOf vs savableState

Jyoti Sheoran
4 min readApr 27, 2023
Photo by Markus Winkler on Unsplash

In Jetpack Compose, there are several ways to manage state, including the remember, derivedStateOf, and savableState functions. Here's a detailed explanation of each function and an example of how to use them:

remember:

remember is a function that allows you to create a variable whose value will be remembered across recompositions.

It is typically used for a state that is local to a Composable function, such as UI state or temporary data.

The value of a remember variable can be changed using a lambda function that is passed to it. Whenever the value of a remember variable changes, the Composable function that uses it will be recomposed with the new value.

For example, consider a TextField Composable function that allows the user to enter a name:

@Composable
fun NameInput(onNameChanged: (String) -> Unit) {
var name by remember { mutableStateOf("") }

TextField(
value = name,
onValueChange = {
name = it
onNameChanged(it)
},
label = { Text("Enter your name") }
)
}

In this example, we define a NameInput Composable function that contains a TextField that allows the user to enter a name. We use a remember variable called name to hold the current value of the name. Whenever the user types into the TextField, the name variable is updated, which triggers a recomposition of the NameInput Composable function with the new value.

derivedStateOf:

derivedStateOf is a function that allows you to create a variable whose value is derived from other state variables. It takes a lambda function that defines how to derive the value of the variable based on other state variables. Whenever any of the state variables used in the lambda function changes, the derived state variable will be recomputed.

derivedStateOf is useful when you want to compute a value based on other state variables without recomputing it every time the Composable function is recomposed.

For example, consider a Composable function that displays the sum of two numbers:

@Composable
fun AddNumbers(a: Int, b: Int) {
val sum = remember(a, b) { derivedStateOf { a + b } }

Text("Sum: $sum")
}

In this example, we define an AddNumbers Composable function that takes two integers, a and b, as input. We use a derivedStateOf variable called sum to hold the sum of a and b. Whenever either a or b changes, the sum variable will be recomputed with the new value, but the AddNumbers Composable function will not be recomposed unless any other state in the function changes.

savableState:

savableState is a function that allows you to save and restore state across configuration changes, such as a screen rotation. It takes a lambda function that defines how to save and restore the state. The value of a savableState variable is automatically saved and restored by the Android system. savableState is useful when you want to preserve state across configuration changes.

For example, consider a Composable function that allows the user to enter a username and password and saves the credentials:

@Composable
fun LoginScreen() {
val savedUsername = savableState { mutableStateOf("") }
val savedPassword = savableState { mutableStateOf("") }

Column {
TextField(
value = savedUsername.value,
onValueChange = { savedUsername.value = it },
label = { Text("Username") }
)
TextField(
value = savedPassword.value,
onValueChange = { savedPassword.value = it },
label = { Text("Password") }
)
Button(onClick = { /* handle login */ }) {
Text("Log In")
}
}
}

In this example, we use savableState to save and restore the user's entered username and password across configuration changes. We define two savableState variables, savedUsername and savedPassword, that hold the current value of the user's entered username and password, respectively. Whenever the user enters a new username or password, the corresponding savableState variable is updated.

The Android system automatically saves and restores the value of savedUsername and savedPassword across configuration changes, such as a screen rotation. When the Composable function is recomposed after a configuration change, the saved values are restored and used as the initial value of the corresponding TextFields.

Here’s an another example of how to use remember, derivedStateOf, and savableState in a Jetpack Compose app:

@Composable
fun CounterScreen() {
// Define a `remember` variable to hold the current count
var count by remember { mutableStateOf(0) }

// Define a `derivedStateOf` variable to hold the doubled count
val doubleCount = remember(count) { count * 2 }

// Define a `savableState` variable to hold the count across configuration changes
val savedCount = savableStateHolder { count }

// Increment the count when the button is clicked
Button(onClick = { count++ }) {
Text("Count: $count")
}

// Display the doubled count
Text("Double count: $doubleCount")

// Display the saved count
Text("Saved count: $savedCount")
}

In this example, we define a Composable function called CounterScreen that contains three different ways of managing state: remember, derivedStateOf, and savableState.

First, we define a remember variable called count to hold the current count. Whenever the count variable changes, the CounterScreen Composable function will be recomposed with the new value.

Next, we define a derivedStateOf variable called doubleCount to hold the doubled count. This variable depends on the count variable, so whenever the count variable changes, the doubleCount variable will be recomputed.

Finally, we define a savableState variable called savedCount to hold the count across configuration changes. This variable is automatically saved and restored by the Android system, so the count will be preserved even if the user rotates their device.

When the user clicks the button, the count variable is incremented, which triggers a recomposition of the CounterScreen Composable function. The updated count, doubled count, and saved count are then displayed using the Text Composable function.

I hope these examples and explanations helps you understand how to use remember, derivedStateOf, and savableState in a Jetpack Compose app!

--

--