Reputation: 21
I'm struggling with my app for university and I can't find out why it behaves like it does. I have a EventDetail screen on which the user can join an event. If he does, he gets added to the MutableList Members. I implemented a Membercount on the detail Screen aswell as an if statement to switch the button to leave if the user is already member of the event. Below, you can find a shortened example of the problem. Can somebody tell me what I am doing wrong?
ViewModel
class EventsViewModel(private val eventRepository:
EventRepository = EventRepository()) :
ViewModel() {
var eventList = mutableStateOf<MutableList<Event>>(mutableListOf())
var currentEvent = mutableStateOf<Event?>(null)
fun joinEvent() {
if (currentEvent.value == null) return
viewModelScope.launch {
withContext(Dispatchers.IO) {
val event = eventRepository.joinEvent(currentEvent.value!!)
currentEvent.value = event
}
}
}
}
Composable
fun EventDetailScreen(navController: NavController, eventId: String) {
val viewModel: EventsViewModel = viewModel()
val userViewModel: userViewModel = viewModel()
viewModel.getEvent(eventId)
val event by remember {
viewModel.currentEvent
}
if (event!!.createdBy != userViewModel.currentUser.value!!.uid)
if (event!!.members.find { it == userViewModel.currentUser.value!!.uid } == null)
Button_Primary(
onClick = { viewModel.joinEvent() },
text = "Join Event",
)
else
Button_Secondary(
onClick = { viewModel.leaveEvent() },
text = "Leave Event",
)`
Event
data class Event(
val id: String?,
val title: String,
val description: String,
val startDateTime: LocalDateTime,
val endDateTime: LocalDateTime,
val location: String?,
val createdBy: String?,
val createdAt: LocalDateTime = LocalDateTime.now(),
var members: MutableList<String> = mutableListOf(),
val currentIds: MutableList<String> = mutableListOf()
)
I already found, that members can be set to MutableStateOf<MutableList> but I thought, that this should only be done in the UI, as I don't want to send a mutableState to Firebase.
I would expect the button to switch to Leave Event after I joined and switch to Join if I left the event. But it only gets updated when I close the screen. I can see the button change to the right state 1 second before i finally leave the screen.
Upvotes: 1
Views: 1845
Reputation: 93834
You can't make a working State out of a mutable type like MutableList or your Event class (which is mutable because it has some vars
and MutableLists
inside it). Compose cannot detect mutations to these mutable instances. (In general, even without Compose, you would pretty much never combine var
with a mutable type either because it makes something mutable in two different ways, which is error-prone.) For lists, use mutableStateListOf
instead of a List inside a MutableState. Inside your Event class, change the var
to val
and the MutableLists into read-only Lists.
You should also mark the Event class with @Immutable
so the read-only Lists won't cause unnecessary recompositions. But it is up to you not to reuse MutableLists as arguments that you pass to the Event constructor, or you are breaking the @Immutable
contract.
Finally, this code in your composable:
val event by remember {
viewModel.currentEvent
}
means that event
will always be whatever the value of viewModel.currentEvent
was the first time the function was called, so it can never see newer values. Change it to:
val event by viewModel.currentEvent
You don't need to remember things that you retrieve from a ViewModel because the ViewModel outlives your UI.
Upvotes: 1