State Management in Jetpack Compose

Working with Jetpack Compose in Android, and want to update UI on run time with some newly updated data. This can be handled by managing the state in Jetpack Compose.

In this tutorial, we are going to learn how to manage state in Jetpack Compose. We will learn,

  • What is a state in Jetpack Compose?
  • How does recompose work?
  • Different ways to manage states in Compose

To learn how to build UI in Jetpack Compose, click here

Watch Jetpack Compose Full Video Tutorial, click here

What is a state in Jetpack Compose?

State in general term is an object which contains certain data that is mapped to one or many widgets. Using the value from the state object we can update the data shown in the widgets. The value of the state can change during the runtime, and this will help us to update the widget with the updated data.

In Jetpack Compose, the composable update itself based on the value of the state. When the value is updated, the composable function only re-composes the composable whose data is updated and ignores the rest of them.

In Jetpack Compose, the composable are subscribed to a state and when the value of the state is updated then all the composable who are subscribed to it also updates the value.

Here, all three texts are subscribed to a state, and when the value of state changes, the text in these three would also be updated.

How does recompose work?

While recomposing the UI, the UI tree doesn't redraw itself but only updates the specific composable because if we redraw the whole UI, again and again, it would be a very expensive task.

But, how does it know which composable to update?

Jetpack Compose works on the concept of Positional Memoization, which is an optimization technique used primarily to speed up programsfunction calls and returning the cached result. In Compose, when we draw the UI for the first time then, it caches all the composable in UI tree.

When the value in the state is updated, then the node gets updated that subscribes to that state and not the whole UI. This will only recompose the specific node in the UI tree.

Different ways to manage states in compose

In Jetpack Compose, we can manage state by two different ways,

  • MutableState - In this, the state stores the value on execution, and if any composable is subscribed to it, the composable updates the value if there are any changes.
  • Model - We use Model as an annotation to any class which will help us to update the UI.

Now, let us understand how we can use both of them.

We will be working with an example with both MutableState and Model. But first, let's create an application and do the basic setup. Our MainActivity would look like,

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MaterialTheme {
                App()
            }
        }
    }


    @Composable
    fun App() {
        
    }
}

In this, App composable function we are going to update our state. In this application, we want to design a text and a Button. When we click the button the text increments the value by 5 each time.

So, now let's start implementing.

Using MutableState in Compose

First, we will create a composable function called,

setupStateUsingState()

and add it in App like,

@Composable
fun App() {
    setupStateUsingState()
}

Here, in setupStateUsingState() we will write our basic structure,

@Composable
private fun setupStateUsingState() {
    Column {
        TopAppBar(
            title = { Text(text = "State Management") }
        )
        Text(
            text = //set new value
        )
        Button(onClick = {
            //update value
        }) {
            Text(text = "Click to Add 5")
        }
    }

}

Here, we have added basic composable of a TopAppBar, Text, and Button. In Button, I have added, the onClick where will update the value and in Text, we will update the value.

Now, we will create a variable of type MutableState which will hold an initial value of 0. When we click the button we will add 5 to the value and the state would re-compose to redraw the UI.

var counterState: MutableState<Int> = state { 0 }

MutableState gives us the value property on execution. The Composable function subscribes to the MutableState and when the value changes, the re-composition of the composable happens and hence the value gets updated.

If the value is unchanged, re-composition won't happen.

Here, in our example, Text will subscribe to the value property of counterState and on button click, we want to update the value of counterState by 5.

So, we will update the code like,

@Composable
private fun setupStateUsingState() {
    Column {
        TopAppBar(
            title = { Text(text = "State Management") }
        )
        var counterState:MutableState<Int> = state { 0 }
        Text(
            text = counterState.value.toString()
        )
        Button(onClick = {
            counterState.value += 5
        }) {
            Text(text = "Click to Add 5")
        }
    }

}

Here, we first created a variable counterState of type MutableState and set an initial value of 0. When we click the button we update the value by 5 and the Text here observes the changes to the value of counterState.

So, when on the click of a button the value is updated, the Text also re-composes itself to show the updated value.

Using Model in Compose

The other way to handle state changes in Jetpack compose is to use a Model. Model in Jetpack Compose is an annotation which we can use in any class. If any composable gets a value from any parameter of class which is annotated by @Model, and if the value of parameter changes, the UI also recomposes itself.

For the above example, let us create a composable function again,

setupStateUsingModel()

and in this let's add the basic code again,

@Composable
private fun setupStateUsingModel() {
    Column {
        TopAppBar(
            title = { Text(text = "State Management") }
        )
        Text(
            text = //set new value
        )
        Button(onClick = {
            //update value
        }) {
            Text(text = "Click to Add 5")
        }
    }

}

Now, let us create a data class Counter with a parameter value of type Int and initial value 0. We will annotate the class with @Model,

@Model
data class Counter(var value: Int = 0)

Now, we will use the value parameter of Counter class to update the value in Text.

So, first I will create the object of the class,

val counter = Counter()

and, then will update the value on click of the button like,

Button(onClick = {
    counter.value += 5
}) {
    Text(text = "Click to Add 5")
}

and since we are observing the value in our text, we will update the value in Text as well by,

Text(
    text = counter.value.toString()
)

So, my whole setupStateUsingModel() looks like,

@Composable
private fun setupStateUsingModel() {
    val counter = Counter()
    Column {
        TopAppBar(
            title = { Text(text = "State Management") }
        )
        Text(
            text = counter.value.toString()
        )

        Button(onClick = {
            counter.value += 5
        }) {
            Text(text = "Click to Add 5")
        }
    }
}

And now when I run the app, my app looks like,

This would be the output when managing the state using MutableState and Model both.

Happy learning.

Team MindOrks :)

Also, Let’s connect on Twitter, Linkedin, Github, and Facebook