I have a fragment that shows a location after collecting it from the viewmodel stateflow, the location is stored in shared preferences than emitted to a mutableflowstate when the fragment starts.
The fragment can show a BottomSheetDialogue, in the BottomSheetDialog the user can update the location, I am updating the location via the viewmodel in both shared prefs and the mutablestateflow.
The problem is when the new location is updated, and the bottom sheet is dismissed the new value is not collected in the fragment, or the collector is not notified.
I am not sure exactly what I am doing wrong here.
Fragment
val viewModel: LocationViewModel by viewModels() lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.shippingLocation.collect { location ->
if (location != null) {
binding.shippingLocation.text = location
} else {
binding.shippingLocation.text = ""
}
}
}
}
binding.shippingLocationEditBtn.setOnClickListener {
val editLocationSheet = EditLocationFragment()
editLocationSheet.show(parentFragmentManager, "EditLocationSheet")
}
ViewModel
private val _shippingLocation = MutableStateFlow<String?>(null)
val shippingLocation: StateFlow<String?> = _shippingLocation.asStateFlow()
init {
getShippingLocation()
}
fun updateShippingLocation(location: String?) {
sharedPreferences.edit()
.putString(PREFS_SHIPPING_LOCATION_KEY, location)
.apply()
_shippingLocationWilaya.value = location
}
private fun getShippingLocation() {
val location = sharedPreferences.getString(PREFS_SHIPPING_LOCATION_KEY, null)
viewModelScope.launch {
if (location == null) {
_shippingLocation.value = ""
} else {
_shippingLocation.value = location
}
}
}
BottomSheetDialog
val viewModel: LicationViewModel by viewModels()
binding.editShippingLocationTopAppBar.setOnMenuItemClickListener { menuItem ->
when (menuItem.itemId) {
R.id.saveShippingLocation -> {
if (location != null) {
viewModel.updateShippingLocation(location)
dismiss()
} else {
Toast.makeText(
requireContext(),
"Please select a location",
Toast.LENGTH_SHORT
).show()
}
true
}
else -> false
}
}
You are collecting the value only on the view started state which means the value will be collected only when the view started not every time it changes its value.
You need to collect the value every time it changes.