I'm working with Flow
and MutateStates
in Kotlin. I have a list of Data
in my viewModel
that corresponds to a couple of different fragments. As per my previous SO question, Only updated necessary components in MutableStateFlow<MutableList>, I used the suggested solution to have each individual fragment begiven its own flow. However, my fragments aren't being updated after my data has changed. I'm wondering if I'm doing anything incorrectly. Here's my code:
AppViewModel
class AppViewModel : ViewModel() {
var dataList: MutableStateFlow<List<DeviceData>> = MutableStateFlow(listOf());
fun getDataForFragment(index: Int): Flow<DeviceData> =
dataList.map { it[index] }.distinctUntilChanged()
fun updateStateFlow(index: Int, data: DeviceData) {
Log.d("stateFlow", "updateStateFlow")
dataList.value = dataList.value.toMutableList().apply {
this[index] = data
}
Log.d("stateFlow", dataList.value.get(index).toString())
}
fun addStateFlow() {
var deviceData_ = DeviceData()
dataList.value = dataList.value.toMutableList().apply {
this.add(deviceData_)
}
}
}
DeviceFragment
class DeviceFragment : Fragment() {
private val viewModel: AppViewModel by activityViewModels()
private var _binding: FragmentDeviceBinding? = null
private val binding get() = _binding!!
private var index: Int? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
@RequiresApi(Build.VERSION_CODES.O)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentDeviceBinding.inflate(inflater)
val bundle = this.arguments
index = bundle!!.getInt("index", 0)
return binding.root
}
@RequiresApi(Build.VERSION_CODES.O)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.viewModelScope.launch {
observers()
}
}
private fun observers() {
lifecycleScope.launchWhenResumed {
index?.let {
viewModel.getDataForFragment(it).collect {
binding.circularProgressView.setProgress(it.progress)
binding.apply {
...
}
}
}
val THRESHOLD = 51.0f
index?.let { it ->
viewModel.getDataForFragment(it).debounce(500)?.collect {
val value = it.probability;
if (value != 0.0F) { //avoid invoking at start of app
if (value > THRESHOLD) {
Log.d("stateFlow", "SENSOR DETECTED")
sensorDetected()
}
}
}
}
}
}
private suspend fun sensorDetected() {
if (index?.let { viewModel.getDataForFragment(it).first().detectCount } == 0) {
Log.d("stateFlow", "sensor invoked")
updateDetectorUI()
}
}
private suspend fun updateDetectorUI() {
when (index?.let { viewModel.getDataForFragment(it).first().detectCount }) {
0 -> {
binding.circularProgressView.setProgress(100f)
changeTintColors(R.color.tint_green)
}
1 -> {
changeTintColors(R.color.tint_purple)
}
2 -> changeTintColors(R.color.tint_purple)
3 -> changeTintColors(R.color.tint_red)
else -> {
binding.circularProgressView.setProgress(100f)
changeTintColors(R.color.tint_green)
}
}
}
override fun onDestroy() {
super.onDestroy()
_binding = null
}
}
When I run my app, I get the logs in updateStateFlow with the correct data. However, no logs from DeviceFragment
shows up despite it making sense to do so (the values pass the conditions in the code).
UPDATE: it seems like there's something wrong with the way I update my flow, but I can't figure out what's wrong :/
I don't know if this will help and to be honest I'm not expert in flows and state but I usually do state flow things like this in the view model:
private val _someValue = MutableSateFlow(listof(failedTimeTravelAttempts))
val someValue = _someValue.asStateFlow()
fun updateSomeValue(input: List<TimeTravelStuff>) {
_someValue.value = input
}
When I need the use this value and have it updated say it's contained in the view model I use it like:
// collectAsState() may be what you need to use?
val mSomeValue = mViewModel.someValue.collectAsState()
// then when I need it
mSomeValue.value
And updating, which I can imagine you know this but for anyone not seeing where this is going:
val mNewList = listOf(secretsToTimeTravel)
mViewModel.updateSomeValue(mNewList)