
Almost every app updates the UI on the run time. In Jetpack Compose, we do it by managing the state. Today, we will learn the concept of state and how to handle it with the help of examples.
Prerequisites:
- Higher Order Functions in Kotlin
- Modifier in Jetpack Compose
- Basics of ViewModel in Android
What is State in Jetpack Compose?
State is any value that can change over time. It decides what should be displayed in the UI.
For this article, we will implement the following button.

When you tap on the button, the text is getting changed. In other words, the state is getting changed (or updated). Let’s make this button.
First, create an empty Jetpack Compose project and open MainActivity. Create a MyUI() composable and call it from the onCreate() method.
// add the following imports
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
YourProjectNameTheme(darkTheme = false) {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
MyUI()
}
}
}
}
}
}
@Composable
fun MyUI() {
}
Add a button in the MyUI().
@Composable
fun MyUI() {
Button(
onClick = { },
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.Yellow
)
) {
Text(text = "")
}
}
Run it. It shows a blank button with a yellow background.

We have to show the number of clicks. So, Create a count variable and increment it in the onClick block. Display its value with the Text() composable.
@Composable
fun MyUI() {
var count = 0
Button(
onClick = { count++ },
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.Yellow
)
) {
Text(text = "Count $count")
}
}
Run the project. If you tap on the button, the count value will never increase.

To understand what is going on, log the count value before the Button() and inside the onClick block.
@Composable
fun MyUI() {
var count = 0
Log.d("Before Button()", "Count = $count")
Button(
onClick = {
count++
Log.d("Inside onClick", "Count = $count")
},
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.Yellow
)
) {
Text(text = "Count $count")
}
}
If you run the app, it displays Before Button(): Count = 0 first, and every time you click on the button, the count will be incremented.
Our UI is not getting updated even if the count value is increased. This is because we are using normal Kotlin variables. We have to use the objects of type MutableState. It holds a value and whenever the value is changed, it updates the UI (corresponding composables).
In Jetpack Compose, we can create a MutableState object in the following way.
var count by remember { mutableStateOf(0) }
Write it at the beginning of the MyUI().
@Composable
fun MyUI() {
var count by remember { mutableStateOf(0) }
Button(
onClick = {
count++
},
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.Yellow
)
) {
Text(text = "Count $count")
}
}
Run the app. You will get the expected output.

Let us understand the code.
mutableStateOf():
It is a function that accepts a value and creates a new MutableState object. It initializes the object with the passed value and returns it.
remember:
It is a composable function. It helps us to store a single object in memory. A value computed by remember is stored during the initial composition, and returned whenever we update the UI.
For example, when you first call the MyUI(), 0 is stored in the memory because 0 is the default value we sent to mutableStateOf() method. When you click on the button, 0 is returned and 1 is added to it. The result is stored in the memory. When you tap on the button second time, 1 is returned and 1 is added to it. The process gets repeated again and again. The old values get replaced by the new values.
remember can be used to store both mutable and immutable objects.
by Delegate:
In simple words, it converts the MutableState object into a regular Kotlin variable.
In the above code, mutableStateOf() returns MutableState object. The by Delegate changes it to Int. Place the mouse over the count variable. You will see its type.

If you don’t want to use by Delegate, you can also use =.
var count = remember { mutableStateOf(0) }
But, you have to use count.value to update or get the count value.
@Composable
fun MyUI() {
var count = remember { mutableStateOf(0) }
Button(
onClick = {
count.value++
},
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.Yellow
)
) {
Text(text = "Count ${count.value}")
}
}
mutableStateOf() accepts other data types also.
// to store string
var myString by remember {
mutableStateOf("Some string")
}
// to store a float value
var myFloat by remember {
mutableStateOf(0f)
}
ViewModel with Jetpack Compose:
You don’t have to use MutableState to save the state in Jetpack Compose. We can also use LiveData. But, we should convert it to State using observeAsState() method for updating the UI.
First, open this page and search for ViewModel and LiveData related dependencies. Add them to your gradle files.
Add the following imports in the MainActivity.
import androidx.compose.runtime.livedata.observeAsState
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewmodel.compose.viewModel
Next, create a class and name it MyViewModel. It should extend ViewModel().
class MyViewModel : ViewModel() {
}
Next, declare a count variable with type MutableLiveData. Create an increment() function and increase the count value by 1 in it.
class MyViewModel : ViewModel() {
var count = MutableLiveData<Int>(0)
fun increment() {
count.value = count.value?.plus(1)
}
}
In the MyUI() method, add a parameter of type MyViewModel.
@Composable
fun MyUI(myViewModel: MyViewModel = viewModel()) {
}
Add the following line at the beginning.
val clickCount by myViewModel.count.observeAsState(0)
Here, myViewModel.count returns the count variable (which is of type MutableLiveData). observeAsState() converts it to State object. The by delegate changes this State object to a normal Koltlin variable. We have assigned it to clickCount.
In the onClick block, call myViewModel.increment() method so that our clickCount will be increased by 1.
@Composable
fun MyUI(myViewModel: MyViewModel = viewModel()) {
val clickCount by myViewModel.count.observeAsState(0)
Button(
onClick = {
myViewModel.increment()
},
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.Yellow
)
) {
Text(text = "Count $clickCount")
}
}
Run the project. The clickCount will be incremented every time you tap on the button.

Recomposition in Jetpack Compose:
This is a common word you encounter when working with Jetpack Compose. Recomposition is the process of updating the UI when the corresponding state changes. For example, in the above code, the Button() gets redrawn on the screen when the clickCount changes.
Only the composables that depend on the state will be redrawn. For example, look at the following code.
@Composable
fun MyComposable(text1: String, text2: String) {
// This will recompose (re-drawn) when [text1] changes,
// but not when [text2] changes
Text(text = text1)
// This will recompose when [text2] changes,
// but not when [text1] changes
Text(text = text2)
}
Call it from our MyUI().
@Composable
fun MyUI() {
var text1 by remember {
mutableStateOf("")
}
var text2 by remember {
mutableStateOf("")
}
Button(
onClick = {
val randomNumber = (1..20).random()
if (randomNumber % 2 == 0) {
text1 = randomNumber.toString()
} else {
text2 = randomNumber.toString()
}
}
) {
Text(text = "Click")
}
Spacer(modifier = Modifier.height(height = 20.dp))
MyComposable(text1 = text1, text2 = text2)
}
@Composable
fun MyComposable(text1: String, text2: String) {
// This will recompose (re-drawn) when [text1] changes,
// but not when [text2] changes
Text(text = text1)
// This will recompose when [text2] changes,
// but not when [text1] changes
Text(text = text2)
}
Output:

This is all about managing the state in Jetpack Compose. I hope you learned something new. If you have any doubts, comment below.
Related Articles:
- 25+ Awesome UI Designs Made with Jetpack Compose (with Source Code)
- AppLovin MAX Banner Ads Integration in Android
- Animating Content Changes in Jetpack Compose
References: