Jetpack Compose  - 5

Jetpack Compose - 5

In this article let us learn about the states in Jetpack Compose. State is a value that can change. It can be a variable, database value or even the UI elements which respond to events. Keeping track of the state of a variable or the UI element is most widely used feature in any android apps. In Compose, whenever the state changes, re-composition occurs.

Compose provides State and MutableState interfaces that hold the values and trigger UI updates(re-composition). Sometimes, there is a need to guard the state values against recompositions. During that time we need to remember the mutable state using remember function. However remember function cannot survive configuration changes or process deaths, so we have another option rememberSavable which will survive each of the above mentioned changes.

In the below example, on click of a button counter value gets incremented by 1 and displayed on the screen. Here remember is used along with mutable state to keep track of the counter value.

@Composable
fun IncrementMyCounter(modifier: Modifier = Modifier) {
    val counter: MutableState<Int> = remember { mutableStateOf(0) }
    Column(
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally,
        modifier = Modifier.padding(16.dp)
    ) {
        Text(text = "Counter Value - ${counter.value}")
        Spacer(Modifier.padding(8.dp))
        Button(onClick = { counter.value++ }) {
            Text(text = "Add")
        }
    }
}

Stateful and Stateless

A composable that holds the state value (one which uses remember to store an object) is called stateful composable.

A composable that doesn't hold any state is called stateless composable.

State Hoisting

State hoisting is nothing but moving the state from composable to it's caller. Sometimes multiple functions can read/modify single state, such states are moved to common parent/caller by using state hoisting method.

Advantages of State Hoisting:

1.Single source of truth can be achieved by moving states to one caller.

2.Only stateful composables can modify the state value.

3.Hoisted state can be shared with multiple composables.

4.Calling function can decide to ignore or modify the events before changing the state value.

General pattern to follow while hoisting a state is to replace the state with below 2 parameters. Additional events can be included by passing lambdas if required.

a. value: T - the current value to display

b. onValueChange: (T) -> Unit : an event that requests the value to change, where T is the proposed new value.

Let us understand it in a better way by looking at below example code

@Composable
fun IncrementCounterStateless(counter1:Int, onIncrement: () -> Unit, modifier: Modifier = Modifier) {

    Column(
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally,
        modifier = Modifier.padding(16.dp)
    ) {
        Text(text = "Counter Value - $counter1")
        Spacer(Modifier.padding(8.dp))
        Button(onClick = onIncrement ) {
            Text(text = "Add two")

        }

    }

}

@Composable
fun IncrementCounterStateful(modifier: Modifier = Modifier){
    var counter by rememberSaveable { mutableStateOf(0) }
    IncrementCounterStateless(counter, onIncrement= {counter=counter+2}, modifier)
}

In the above code, which increments the counter value, the state value i.e. counter is maintained by calling composable. Stateless composable only reads the value.

Stateful composable function can pass the state value to multiple composables as shown below.

@Composable
fun IncrementCounterStateful(modifier: Modifier = Modifier){
    var counter by rememberSaveable { mutableStateOf(0) }
    IncrementfirstCounter(counter, onIncrement= {counter++}, modifier)
    IncrementsecondCounter(counter, onIncrement= {counter=counter+2}, modifier)
}

Note - Since hoisted state can be shared, we need to be sure to pass only the required states else it will trigger unnecessary recompositions and affect the performance.