Search code examples
viewmodelandroid-jetpack-compose

Jetpack Compose Observe mutableStateOf in ViewModel


I have a need to update the user profile switch

  1. ViewModel
class ProfileViewModel : BaseViewModel() {

    var greet = mutableStateOf(user.pushSetting.greet)
    var message = mutableStateOf(user.pushSetting.message)
    var messageDetails = mutableStateOf(user.pushSetting.messageDetails)

    var follow = mutableStateOf(user.pushSetting)
    var like = mutableStateOf(user.pushSetting.like)
    var comment = mutableStateOf(user.pushSetting.comment)

    fun updateUser() {
        println("--")
    }
}

2.Composable

@Composable
fun SettingCard(viewModel: ProfileViewModel) {

    Lists {
        Section {
            TextRow(text = "手机号码") { }
            TextRow(text = "修改密码", line = false) { }
        }

        Section {
            SwitchRow(text = "新好友通知", checkedState = viewModel.greet)
            SwitchRow(text = "新消息通知", checkedState = viewModel.message)
            SwitchRow(text = "消息显示详细", line = false, checkedState = viewModel.messageDetails)
        }
    }
}

3.SwitchRow

@Composable
fun SwitchRow(text: String, line: Boolean = true, checkedState: MutableState<Boolean>) {

    ListItem(
        text = { Text(text) },
        trailing = {
            Switch(
                checked = checkedState.value,
                onCheckedChange = { checkedState.value = it },
                colors = SwitchDefaults.colors(checkedThumbColor = MaterialTheme.colors.primary)
            )
        }
    )
}

How can I observe the change of the switch and call updateUser() in ViewModel

I know this is a way, but it is not ideal. The network update will be called every time it is initialized. Is there a better solution?

LaunchedEffect(viewModel.greet) {
     viewModel.updateUser()
}

Solution

  • The best solution for this would be to have unidirectional flow with SwitchRow with a lambda as @Codecameo advised.

    But if you want to observe a MutableState inside your Viewmodel you can use snapshotFlows as

    var greet: MutableState<Boolean> = mutableStateOf(user.pushSetting.greet)
    
    init {
        snapshotFlow { greet.value }
            .onEach {
                updateUser()
            }
            .launchIn(viewModelScope)
            //...
    }
    

    Create a Flow from observable Snapshot state. (e.g. state holders returned by mutableStateOf.) snapshotFlow creates a Flow that runs block when collected and emits the result, recording any snapshot state that was accessed. While collection continues, if a new Snapshot is applied that changes state accessed by block, the flow will run block again, re-recording the snapshot state that was accessed. If the result of block is not equal to the previous result, the flow will emit that new result. (This behavior is similar to that of Flow.distinctUntilChanged.) Collection will continue indefinitely unless it is explicitly cancelled or limited by the use of other Flow operators.