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
p
rograms
function 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 :)