I’m relatively new to Android development. While designing the application, I encountered some problems. Everything I found did not quite cover my needs. I would like to listen to your pieces of advice. So, okeeey let’s go!
The main screen displays a list of dishes for a specific category. I would like the icon on the items I added to the database to update.
Model:
data class MealModel(
val id: String,
val name: String,
val image: String,
val isSaved: Boolean = false,
)
Resource via State:
sealed class Resource<T>(
val data: T? = null,
val message: String? = null,
) {
class Success<T>(data: T) : Resource<T>(data)
class Error<T>(message: String?, data: T? = null) : Resource<T>(data, message)
class Loading<T> : Resource<T>()
}
ViewModel:
@HiltViewModel
class HomeViewModel @Inject constructor(
private val getMealsUseCase: GetMealsUseCase,
private val isMealInSavedUseCase: IsMealInSavedUseCase,
private val addOrRemoveMealUseCase: AddOrRemoveMealUseCase,
private val dispatcher: CoroutineDispatcher = Dispatchers.IO,
) : ViewModel() {
private val _meals = MutableStateFlow<Resource<List<MealModel>>>(Resource.Loading())
val meals: StateFlow<Resource<List<MealModel>>> = _meals.asStateFlow()
fun getMeals() {
getMealsUseCase.invoke(category = category)
.onEach { resource ->
when (resource) {
is Resource.Loading -> {
_meals.value = Resource.Loading()
}
is Resource.Success -> {
if (resource.data != null) {
_meals.value = Resource.Success(resource.data)
}
}
is Resource.Error -> {
_meals.value = Resource.Error(resource.message)
}
}
}
.launchIn(viewModelScope)
}
fun savedIconClicked(meal: MealModel) {
viewModelScope.launch(dispatcher) {
addOrRemoveMealUseCase.addOrRemoveMeal(meal)
}
}
}
After searching for an answer, I realized that I could do something like this:
val index = _meals.value.data!!.indexOf(item)
val items = _meals.value.data!!.toMutableList()
items[index] = items[index].copy(isSaved = item.isSaved.not())
_meals.value.data = items
The truth is that the problem arises in Resource
. The data
is val
due to which I cannot assign a new list. If you make a var
everything works according to the logs.
And now inside the function getMeals()
, when the state is successful, it looks like this:
is Resource.Success -> {
if (resource.data != null) {
_meals.value = Resource.Success(resource.data!!)
}
for (item in resource.data!!) {
if (isMealInSavedUseCase.isMealInSaved(item.id)) {
val index = _meals.value.data!!.indexOf(item)
val items = _meals.value.data!!.toMutableList()
items[index] = items[index].copy(isSaved = item.isSaved.not())
_meals.value.data = items
}
}
}
But, firstly, changing data
in Resource
is probably not particularly advisable. Secondly, when requesting again, the list will be updated again with MealModel
elements with the isSaved = false
. That is, we will not notice any attempts to change the added element. And thirdly, I don’t really understand how dynamically the add icon should be updated. Working with list elements is in the adapter, but we cannot add LiveData
there, which will store a specific icon.
Finally, for understanding, I’ll show you what a list element looks like:
Mikhail Dubovikov is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.