Jetpack Compose ViewModel Update UI Using Flow
LiveData is commonly used for reflecting recent changes in data sources in XML-based applications. Also, it is a widely accepted solution for that.
But, when you move on to New Declarative UI Jetpack Compose. You can use LiveData for reflecting changes, but, Flow Is more efficient and reliable to use with Jetpack Commpose.
First We will talk about the architecture based on which we will make our app. Today, Mobile app developers mostly use the Clean Architecture MVVM, and MVI as their preferred app architecture.
- Update Data Using Flow
Note : You can create a new project in Latest Android Studio or Download Source From Github Repo.
We are creating a sample Composables
with Two Buttons
And One Text
. Here is a Row
with Three Child One Button
For Increment The Counter Another For Decrease The Counter And Last One For The Showing Counter A Text
horizontalArrangment
: this will ensure all the components have even spacing between them.verticalAlignment
: show content in the center of the current screen.onClick{}
: we have used the lamdas rather than passing theViewModel
instance to theComposable
so that we. can avoid unnecessary recompositions.
State Hoisting Best Practices : https://developer.android.com/jetpack/compose/state-hoisting
@Composable
fun FlowExample(counter: Int, onAdd: () -> Unit, onRemove: () -> Unit) {
Row(
modifier = Modifier
.padding(horizontal = 10.dp)
.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceAround
) {
Button(onClick = { onAdd() }) {
Text(text = "Add")
}
Box(
modifier = Modifier
.padding(horizontal = 10.dp)
.fillMaxWidth()
.weight(1f)
.clip(shape = RoundedCornerShape(12.dp))
.background(color = Color.Black),
contentAlignment = Alignment.Center
) {
Text(
text = "Counter Value :: $counter ",
modifier = Modifier.padding(vertical = 8.dp),
color = Color.White
)
}
Button(onClick = { onRemove() }) {
Text(text = "Remove")
}
}
}
After creating the composable Comopsables
. letβs code the backbone of the app means Business logic.
Preview
MainViewModel
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
class MainViewModel : ViewModel(){
//Create a flow for counter
private val _counter = MutableStateFlow(0)
//Expose immutable flow using asStateFlow()
val counter = _counter.asStateFlow()
init {
changeCounterValue(0)
}
private fun changeCounterValue(cnt : Int){
_counter.value = cnt
}
fun incrementCounter (){
_counter.value = _counter.value + 1
}
fun decrementCounter(){
_counter.value = _counter.value - 1
}
}
- Create a simple
ViewModel
by extending theViewModel()
and createMutableStateFlow(0)
with an initial value of zero and keep it private so other sources can not edit them onlyViewModel
can edit it. - So we have to create another variable which will be immutable means Read-Only. which we will observe as a state in the
Composables
- There is two functions for incrementing and decrease of the counter which is exposed to the Compsoables.
How to use it
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.md.jetpackcomposeupdateuiusingviewmodel.ui.theme.JetpackComposeUpdateUIUsingViewModelTheme
import androidx.compose.ui.Alignment
import androidx.lifecycle.compose.collectAsStateWithLifecycle
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val mainViewModel: MainViewModel by viewModels()
setContent {
JetpackComposeUpdateUIUsingViewModelTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
val counter by mainViewModel.counter.collectAsStateWithLifecycle()
FlowExample(counter = counter,
onAdd = {
mainViewModel.incrementCounter()
},
onRemove = {
mainViewModel.decrementCounter()
})
}
}
}
}
}
- After creating the design and ViewModel we need to observe the changes so that we can update the UI as the value changes and all this thing will be handled by
MainViewModel
collectAsStateWithLifecycle
used to collect the latest value from theStateFlow
in a lifecycle-aware manner. so whenever it is not required it will bedisposed
Donβt forget to add the following dependency to the gradle for using the
collectAsStateWithLifecycle
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.6.2")
Final App Preview
π΄Check out the complete code on my GitHub Gist. βοΈ Hope this project really helps you.
Hope you enjoy coding Jetpack Compose π. Donβt forget to share π¨ and clap π.
Any Suggestions are welcome. If you need any help or have questions for Code Contact Me. You can follow me on LinkedIn, StackOverflow and Twitter For More Updates π
Happy Compose !! π