State management is a cornerstone of dynamic UIs in Jetpack Compose. Among its many tools, remember
and rememberSaveable
are commonly used to manage state inside composable functions. Although they may appear similar, their behavior differs significantly when it comes to state persistence during lifecycle changes like screen rotations or process recreation.
Let’s explore these differences with practical examples and learn about advanced use cases such as using Bundle
and custom Saver
.
remember
?The remember
function helps store a value in memory across recompositions. However, it does not persist the state during configuration changes, such as screen rotation or process recreation.
remember
Imagine you’re building a simple counter app:
@Composable
fun CounterWithRemember() {
var count by remember { mutableStateOf(0) }
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "Count: $count")
Button(onClick = { count++ }) {
Text("Increment")
}
}
}
Here, the counter value resets to 0
when you rotate the device because remember
only retains the state during the current composition lifecycle.
rememberSaveable
?rememberSaveable
is an extension of remember
that retains the state across configuration changes by saving it into a Bundle, which is part of Android’s saved instance state mechanism.
rememberSaveable
Let’s enhance our counter app to retain its value even after a screen rotation:
@Composable
fun CounterWithRememberSaveable() {
var count by rememberSaveable { mutableStateOf(0) }
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "Count: $count")
Button(onClick = { count++ }) {
Text("Increment")
}
}
}
In this case, the counter value persists even if the screen is rotated because the value is saved into a Bundle and restored automatically.
Yes, you can! If the state is more complex or needs to be shared across multiple composables, using a ViewModel
is often a better approach. Unlike rememberSaveable
, a ViewModel
persists state across configuration changes and process recreation as long as the app is not explicitly killed.
class CounterViewModel : ViewModel() {
var count by mutableStateOf(0)
private set
fun increment() {
count++
}
}
@Composable
fun CounterWithViewModel(viewModel: CounterViewModel = viewModel()) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "Count: ${viewModel.count}")
Button(onClick = { viewModel.increment() }) {
Text("Increment")
}
}
}
Here, CounterViewModel
retains the counter state even if the system kills and recreates the activity.
rememberSaveable
can handle basic data types like String
, Int
, or Boolean
because they are serializable and can be stored in a Bundle. However, for custom or non-primitive data types, you need to use a Saver.
A Saver
helps rememberSaveable
store and restore complex objects by converting them into a format that can be saved into a Bundle (e.g., a Map
or List
).
Define the Object You Want to Save:
Create a data class or object for your state.
Implement a Saver:
Write a Saver
that converts the object into a savable format and restores it when needed.
Use the Saver in rememberSaveable
:
Pass the Saver to rememberSaveable
manage your custom object’s state.
data class User(val name: String, val age: Int)
val UserSaver = Saver<User, Map<String, Any>>(
save = { mapOf("name" to it.name, "age" to it.age) },
restore = { User(it["name"] as String, it["age"] as Int) }
)
@Composable
fun CustomSaverExample() {
var user by rememberSaveable(stateSaver = UserSaver) {
mutableStateOf(User(name = "Akshay", age = 28))
}
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "Name: ${user.name}, Age: ${user.age}")
Button(onClick = { user = user.copy(age = user.age + 1) })
{ Text("Increase Age") }
}
}
Here, the User
object is saved into a Map
(compatible with a Bundle) and restored when needed.
To summarize:
Use remember
for transient state that doesn’t need to persist across configuration changes.
Use rememberSaveable
for state that needs to survive configuration changes, leveraging Bundles.
Use ViewModel
for complex state management or shared state across composables.
Create a Saver for non-primitive objects when using rememberSaveable
.
By combining these tools, you can effectively manage the state in your Jetpack Compose applications, ensuring a smooth and robust user experience.
Akshay Nandwana
Founder AndroidEngineers
You can connect with me on:
Join our upcoming classes
https://www.androidengineers.in/courses