I am setting the value of a variable using the LiveData on my view model. The Data is fetched from Firebase. Below is the ViewModel structure class FarmViewModel(): ViewModel() {
var firestoreDB = InitializeFirestore()
private var _farms = MutableStateFlow<List<FarmEntity>>(emptyList())
private var _farm = MutableLiveData<FarmEntity>()
var farms = _farms.asStateFlow()
var farm : LiveData<FarmEntity> = _farm
init{
getFarms()
}
fun getFarm(id:String){
firestoreDB.collection("farms")
.whereEqualTo("id",id)
.get()
.addOnSuccessListener {
if (it != null) {
//the farm name value is as expected
Log.d("farms", "DocumentSnapshot data: ${it.toObjects<FarmEntity>()[0].farm_name}")
_farm.value=it.toObjects<FarmEntity>()[0]
} else {
Log.d("farms", "No such document")
}
}
.addOnFailureListener{
Log.e("farms",it.message.toString())
setError(
ErrorData(
code=500,
service="CreateFarm",
message = it.message.toString()
)
)
}
}
The getFarm function is called using an Onclick Event that fetches the selected id and also to navigate to the Farm Screen.
On the Composable, the farm value is Null
val farm = farmViewModel.farm.observeAsState()
Log.i("farm_", farm.value.toString())
When you inject FarmViewModel
in different screens with viewModel()
you in fact inject different instances of that ViewModel
, each instance scoped to its own screen. Different instances obviously won't know about values of each other fields.
It is possible to share ViewModel
between navigation destinations, but it is recommended to have one ViewModel
class per screen, e.g. FarmViewModel
for FarmScreen
and FarmsViewModel
for FarmsScreen
.
You can pass farm ID to FarmScreen
with navigation arguments and fetch data on FarmViewModel
creation.
Edit:
Example navigation:
@Serializable
data class Farm(val id: Int)
@Serializable
private object Farms
@Composable
fun FarmsNavigation() {
val navController = rememberNavController()
NavHost(navController, startDestination = Farms) {
composable<Farms> {
FarmsScreen(
onNavigateToFarm = { farmId ->
navController.navigate(Farm(farmId))
},
)
}
composable<Farm> { backStackEntry ->
FarmScreen(
//..
)
}
}
}
FarmViewModel:
class FarmViewModel(
savedStateHandle: SavedStateHandle,
) : ViewModel() {
// Obtain farm id from SavedStateHandle
init {
val farm = savedStateHandle.toRoute<Farm>()
val id = farm.id
}
}
FarmsScreen:
@Composable
fun FarmsScreen(
onNavigateToFarm: (Int) -> Unit,
viewModel: FarmsViewModel = viewModel(),
) {
Button(onClick = { onNavigateToFarm(123) }) {
Text("Go to farm 123")
}
}