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
.
1. What is 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.
Example 1: Using 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.
2. What is 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.
Example 2: Using 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.
3. Can We Use ViewModel for State Persistence?
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.
Example: Using ViewModel
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.
4. What About Complex Objects?
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.
5. What is 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
).
Steps to Create a Custom Saver:
Define the Object You Want to Save:
Create a data class or object for your state.Implement a Saver:
Write aSaver
that converts the object into a savable format and restores it when needed.Use the Saver in
rememberSaveable
:
Pass the Saver torememberSaveable
manage your custom object’s state.
Example: Saving a Custom User Object
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.
Conclusion
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:
Book 1:1 Session here Click Here
Join our classes
https://www.androidengineers.in/courses