Compose uses remember API to store object in memory. Stored value is returned during recomposition . remember helps us retain data across recompostion , but when configuration changes happen all stored values are lost . One way to overcome this is to use rememberSaveable . rememberSaveable saves any value that can be saved in a Bundle , so it will survive configuration changes. But when we are using lot of data , for example a list we can cannot use a rememberSavble beacuse there is limit on amount of data that can be stored in Bundle . So we use ViweModel.
ViewModel provide the ui state and access to the business logic located in other layers of the app. It also survives configuration changes. ViewModel handles events coming from the UI or other layers of the app and updates the state holder based on the events.
We need to add the following dependency in our app level build.gradle to use ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.4.1"First create a ViewModel class , then we can access the ViewModel from the composable using viewModel() function.
class DemoViewModel : ViewModel() { ......} @Composable fun DemoFun1( viewModel: DemoViewModel = viewModel() //cereates a new ViewModel ) { ....} @Composable fun DemoFun2( viewModel: DemoViewModel = viewModel() //returns same instance as in DemoFun1 ) { ....}We should call viewModel() function from screen level composable , that is close to root composable called from activity or fragment . We should never pass down ViewModel instances to other Composables , pass only data and fuctions. viewModel() creates a new ViewModel or returns a existing ViewModel in the given scope. ViewModel is retained as long as the scope is alive.
package com.arun.androidtutsforu.democomposeviewmodel import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.* import androidx.compose.material.* import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.lifecycle.ViewModel import androidx.lifecycle.viewmodel.compose.viewModel import com.arun.androidtutsforu.democomposeviewmodel.ui.theme.DemoComposeViewModelTheme class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { DemoComposeViewModelTheme { Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colors.background ) { CalculateScreen() } } } } } @Composable fun CalculateScreen(composeViewModel: ComposeViewModel = viewModel()){ Column( modifier = Modifier.padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally ) { Text( text = "JetPack Compose Demo", fontWeight = FontWeight.Bold, fontSize = 20.sp, color = Color.Blue ) Spacer(modifier = Modifier.height(32.dp)) TextField( value =composeViewModel.firstNum, onValueChange ={composeViewModel.changeFirstNum(it)} , label = { Text(text = "Enter First Num")} ) Spacer(modifier = Modifier.height(16.dp)) TextField( value = composeViewModel.secondNum, onValueChange = {composeViewModel.changeSecondNum(it)}, label = { Text(text = "Enter Second Num")} ) Spacer(modifier = Modifier.height(16.dp)) Text( text = "The sum is ${composeViewModel.sum}", fontWeight = FontWeight.Bold, fontSize = 20.sp ) Button(onClick = { composeViewModel.valuculate() }) { Text(text = "Calculate") } } }This is our ViewModel , ComposeViewModel
package com.arun.androidtutsforu.democomposeviewmodel import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel class ComposeViewModel : ViewModel() { private val _firstNum = mutableStateOf("") val firstNum :String get() = _firstNum.value private val _secondNum = mutableStateOf("") val secondNum :String get() = _secondNum.value private var _sum = mutableStateOf(0) val sum:Int get() =_sum.value fun changeFirstNum(number: String){ _firstNum.value = number } fun changeSecondNum(number: String){ _secondNum.value = number } fun valuculate() { val num1 = firstNum.toIntOrNull()?:0 val num2 = secondNum.toIntOrNull()?:0 _sum.value = num1 + num2 } }
private val _firstNum = mutableStateOf("") val firstNum :String get() = _firstNum.value private val _secondNum = mutableStateOf("") val secondNum :String get() = _secondNum.value private var _sum = mutableStateOf(0) val sum:Int get() =_sum.valueCreate a private _firstNum variable using mutableStateOf , and expose only firstNum as a String so it's not modified outside the ViewModel.
... onValueChange ={composeViewModel.changeFirstNum(it)} ... onValueChange = {composeViewModel.changeSecondNum(it)} ... text = "The sum is ${composeViewModel.sum}" ... Button(onClick = { composeViewModel.valuculate() })
Comments
Post a Comment