# Android : Compose UI State

## **Introduction**

In Android, the recommended practice is to hold a UI-related state (Compose UI State) in a `ViewModel` rather than in an `Activity` or `Fragment`. This is because **Jetpack Compose** follows a **reactive programming model** where the UI is composed based on the current state. By keeping the UI state in a `ViewModel` ensures that the UI remains independent of the `Android lifecycle`, making it more predictable and easier to manage.

---

## **Breakdown**

Here's a breakdown of why we should hold the UI state in a `ViewModel`:

1. **Lifecycle Independence**: `ViewModels` are `lifecycle-aware components` that survive configuration changes (like *screen rotations*) and are associated with a specific scope, often the hosting `Activity` or `Fragment`. This ensures that your UI state persists across *configuration changes*, providing a smoother user experience.
    
2. **Separation of Concerns**: `ViewModel` helps in separating the business logic and UI presentation. Your UI code can focus on describing how the UI should look based on the current state, while the `ViewModel` manages the data and logic required to drive that UI state.
    
3. **Testing**: It's easier to unit test a `ViewModel` as compared to testing UI components. You can write tests to verify the behavior of your business logic and state management without dealing with UI-related complexities.
    
4. **Reusability**: Keeping the UI state in a `ViewModel` makes it more reusable across different `Composable` functions or even different screens, improving code modularity.
    
5. **Compose Design Philosophy**: `Jetpack Compose` encourages a *declarative* UI approach, where we describe what the UI should look like based on the current state. Storing the state in a `ViewModel` aligns well with this design philosophy.
    

---

## Note

However, it's important to note that not all state needs to be stored in a `ViewModel`. UI state that is purely ephemeral and doesn't need to survive configuration changes (like animations, and temporary visual changes) can still be stored within a Composable using `remember` or other Composable-specific state management mechanisms.

---

## **Example**

Let's say we have a simple counter app where you want to display a counter value and allow the user to increment it. We'll use a `ViewModel` to manage the counter state.

1. First, let's create a `ViewModel` that holds the counter value for us:
    

```kotlin
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch

class CounterViewModel : ViewModel() {
    // State flow to hold the counter value
    private val _counterState = MutableStateFlow(0)
    val counterState = _counterState
    
    // Function to increment the counter
    fun incrementCounter() {
        viewModelScope.launch {
            _counterState.value++
        }
    }
}
```

1. In our `Composable` function, we can use the `counterState` from the `ViewModel` to display the counter value and use the `incrementCounter` function to handle user interactions:
    

```kotlin
import androidx.compose.foundation.layout.Column
import androidx.compose.material.Button
import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.viewmodel.compose.viewModel

@Composable
fun CounterScreen() {
    // Access the ViewModel
    val counterViewModel: CounterViewModel = viewModel()
    
    // Observe the counter state
    val counterState by counterViewModel.counterState.collectAsState()

    // Access the context (for showing toasts, etc.)
    val context = LocalContext.current

    Column {
        // Display the counter value
        Text(text = "Counter: $counterState")
        
        // Button to increment the counter
        Button(onClick = { 
            counterViewModel.incrementCounter()
            // Show a toast to indicate the increment
            Toast.makeText(context, "Counter incremented", Toast.LENGTH_SHORT).show()
        }) {
            Text("Increment")
        }
    }
}
```

`CounterViewModel` holds the counter state using a `MutableStateFlow`. The `CounterScreen` Composable function uses the `viewModel` composable to access the ViewModel. It then observes the `counterState` using `collectAsState` and updates the UI whenever the state changes. The button click calls the `incrementCounter` function from the ViewModel to update the counter value.

By following this approach, we ensure that the UI state (`counterState`) is kept separate from the UI presentation logic, making the code easier to understand, test, and maintain.

---

## **Conclusion**

While `ViewModel` is the recommended place to hold the UI state in **Jetpack Compose,** you should consider the nature of the state and its lifecycle requirements when making a decision. Always strive to keep your UI logic separate from your UI presentation to create maintainable and robust applications.

---

That's it for today. Happy Coding...

---

> If get to know something new by reading my articles, don't forget to endorse me on [LinkedIn](https://www.linkedin.com/in/rommansabbir/)
> 
> Read about [SOLID Principles](https://rommansabbir.com/series/solid-principles)
> 
> Read about [Android Development](https://rommansabbir.com/series/android)
