here any one access to view model will be able to ...
# compose
a
here any one access to view model will be able to modify this directly. Which live data i use this pattern
Copy code
private val _quickLinks = MutableLiveData<List<UIQuickLink>>()
val quickLink: LiveData<List<UIQuickLink>> = _quickLinks
So that, quickLink access are only observed than posted with new values from UI, here update to quickLink happens from only view model
f
You can do the same for state, just expose a
State
instead of a
MutableState
using a second property. You could also do it with a function instead of a property
a
@f.babic, is this how we do it ? I understood your point, however some thing is missing, this isn't working
val _registeredUsers = mutableStateListOf<UIRegisteredUser>()
val registeredUser: State<UIRegisteredUser> = _registeredUsers
I think this is how we have to do is in case of mutableStateList
Copy code
val _registeredUsers = mutableStateListOf<UIRegisteredUser>()
val registeredUser: SnapshotStateList<UIRegisteredUser> = _registeredUsers
Copy code
SnapshotStateList
Is mutable 😕
f
Is there a reason why you use
mutableStateListOf
instead of
mutableStateOf
? We're using the latter in our code and seems to work fine, haven't had issues per se.
a
Copy code
private val _registeredUsers = mutableStateOf<List<UIRegisteredUser>>(value = listOf())
val registeredUser: State<List<UIRegisteredUser>> = _registeredUsers
This above appears to work , however following use case we are not able to accomplish with above approach, where as with mutableStateListOf() these worked fine, however, object became insecure
Copy code
init {
    _registeredUsers.value.toMutableStateList().addAll(mockRegisteredUsers)
    
}
Copy code
fun onUserSelected(user: UIRegisteredUser) {
    viewModelScope.coroutineContext.run {
        _registeredUsers.value.toMutableStateList().let { userList ->
            userList.find { it.isSelected }?.let {
                userList.indexOf(it).also { index ->
                    userList.remove(it)
                    userList.add(index, it.copy(isSelected = false))
                }
            }

            userList.find { it == user }?.let {
                userList.indexOf(it).also { index ->
                    userList.remove(it)
                    userList.add(index, it.copy(isSelected = true))
                }
            }
        }
    }
}
f
Oh okay that makes sense - in that case you'd come back to the same thing if you went that way. I mean, instead of:
Copy code
_registeredUsers.value.toMutableStateList().addAll(mockRegisteredUsers)
You could have:
Copy code
_registeredUsers.value = _registeredUsers.value + mockRegisteredUsers
Basically, instead of havign a mutable list that can be changed anywhere, you use immutable lists but make sure to add/remove items from the given list and producing a new list instead. That gives you control in the ViewModel of whatever you want to do, but the rest of the world won't be able to mutate incorrectly
a
@f.babic i got it working with your approach, so these are the changes,
Copy code
private val _registeredUsers = mutableStateOf<ArrayList<UIRegisteredUser>>(value = arrayListOf())
val registeredUsers: State<List<UIRegisteredUser>> = _registeredUsers
I'm using ArrayList now
Copy code
fun onUserSelected(user: UIRegisteredUser) {
    viewModelScope.coroutineContext.run {
        _registeredUsers.value.let { userList ->
            userList.find { it.isSelected }?.let {
                userList.indexOf(it).also { index ->
                    userList.remove(it)
                    userList.add(index, it.copy(isSelected = false))
                }
            }

            userList.find { it == user }?.let {
                userList.indexOf(it).also { index ->
                    userList.remove(it)
                    userList.add(index, it.copy(isSelected = true))
                }
            }
        }
    }
}
this also work fine
however @f.babic there is a catch , when the onUserSelected is called, recomposition is not happening
any input on this ?
f
This is because you're taking the list of items form the State, copying it and updating it in that instance. You should always push the new state/list to the property like:
Copy code
_registeredUsers.value = newValue
when you mutate the list within those
find
calls, you need to make sure to push the new state to the UI
a
@f.babic sorry to bother you this way 😢 that didn't work either UI is observing from composable for registeredUser
Copy code
SelectLoginUsersScreenContent(registeredUsers = loginViewModel.registeredUsers.value, onUserSelected = onUserSelected)
Copy code
fun onUserSelected(user: UIRegisteredUser) {
    viewModelScope.coroutineContext.run {
        _registeredUsers.value.let { userList ->
            userList.find { it.isSelected }?.let {
                userList.indexOf(it).also { index ->
                    userList.remove(it)
                    userList.add(index, it.copy(isSelected = false))
                }
            }

            userList.find { it == user }?.let {
                userList.indexOf(it).also { index ->
                    userList.remove(it)
                    userList.add(index, it.copy(isSelected = true))
                }
            }
            _registeredUsers.value = userList
        }
    }
}
See that last line i 'm even manually setting _registeredUser.value
f
Could you try doing something like:
Copy code
val registeredUsers by loginViewModel.registeredUsers

SelectLoginUsersScreenContent(registeredUsers = registeredUsers, onUserSelected = onUserSelected)
Basically the way we expose state in our VMs is the following:
Copy code
private var threadMessagesState: MessagesState by mutableStateOf(MessagesState())
And then we just fetch it using an exposed property or something
a
Copy code
val registeredUsers by loginViewModel.registeredUsers

SelectLoginUsersScreenContent(
    registeredUsers = registeredUsers,
    onUserSelected = onUserSelected
)
😭 didnt work @f.babic
a
Why can’t you just do this?
Copy code
private val _registeredUsers = mutableStateListOf<UIRegisteredUser>()
val registeredUsers: List<UIRegisteredUser> = _registeredUsers
☝️ 2
a
i need registeredUsers to trigger recomposing when value changes
will it be possible if we use registeredUsers: List<UIRegisteredUser>?
a
Yep. As long as a snapshot state is read, even if not directly, it will trigger recomposition when it changes.
a
💥🥳
@Albert Chang @f.babic thank you , it worked
Copy code
private val _registeredUsers = mutableStateListOf<UIRegisteredUser>()
val registeredUsers: List<UIRegisteredUser> = _registeredUsers
❤️ 1