StateFlow已更新但未收集

A.D*_*ibo 0 android kotlin android-jetpack-compose kotlin-stateflow

我目前正在学习新的 Android 堆栈(MVVM、compose、kotlin Flow/StateFlow),并且在调试值更新的 StateFlow 时遇到问题,但我没有从可组合项收集的迹象。

这是一个普遍的问题,但我通过自己的搜索没有找到任何解决我的问题的方法。

有人知道什么会干扰 StateFlow 吗?我让我的代码如下:

视图模型:

@HiltViewModel
class AuthViewModel @Inject constructor(
    private val navigationManager: NavigationManager,
    private val interactor: AuthInteractor
): BaseViewModel() {

    companion object {
        val TAG: String = AuthViewModel::class.java.simpleName
    }

    private val _uiState = MutableStateFlow(AuthenticationState())
    val uiState: StateFlow<AuthenticationState> = _uiState

    fun handleEvent(event: AuthenticationEvent) {
        Log.v(TAG, "new event: $event")
        when (event) {
            is AuthenticationEvent.GoToRegistering -> navigateToRegistering()
            is AuthenticationEvent.Register -> registerAccount(event)
            is AuthenticationEvent.SnackbarMessage -> showSnackBar(event.message, event.type)
        }
    }

    private fun navigateToRegistering() {
        navigationManager.navigate(NavigationDirections.Authentication.registering)
    }

    private fun registerAccount(event: AuthenticationEvent.Register) {
        Log.v(TAG, "register account")
        _uiState.value.build {
            isLoading = true
        }

        viewModelScope.launch {
            Log.v(TAG, "launching request")
            val exceptionMessage = interactor.registerUser(event.login, event.password)
            Log.v(TAG, "response received, launching state from viewmodel")

            _uiState.value.build {
                exceptionMessage?.let {
                    Log.v(TAG, "Exception is not null")
                    isFailure = true
                    snackbarMessage = interactor.selectRegisterError(it)
                }
            }
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

用户界面组件:

@Composable
fun Authentication(
    modifier: Modifier = Modifier,
    viewModel: AuthViewModel,
    type: AuthenticationType
) {
    val state by viewModel.uiState.collectAsState()
    Log.v("Authentication", "new state: $state, type = $type")

    BackHandler(enabled = true) {
        viewModel.handleEvent(AuthenticationEvent.Back)
    }

    state.snackbarMessage?.let { resourceId ->
        val message = stringResource(resourceId)
        Log.e("Authentication", "error: $message")
        viewModel.handleEvent(AuthenticationEvent.SnackbarMessage(message, SnackbarType.ERROR))
    }

    if (state.isAuthenticated) {
        // TODO: launch main screen
    }

    LaunchedEffect(key1 = state.isRegistered) {
        // TODO: launch login event
    }

    AuthenticationContent(
        modifier = modifier,
        type = type,
        viewModel = viewModel,
        state = state
    )
}
Run Code Online (Sandbox Code Playgroud)

状态数据类:

data class AuthenticationState(
    val isLoading: Boolean = false,
    val isFailure: Boolean = false,
    val isRegistered: Boolean = false,
    val isAuthenticated: Boolean = false,
    @StringRes val snackbarMessage: Int? = null
)  {

    fun build(block: Builder.() -> Unit) = Builder(this).apply(block).build()

    class Builder(uiModel: AuthenticationState) {
        var isLoading = uiModel.isLoading
        var isFailure = uiModel.isFailure
        var isRegistered = uiModel.isRegistered
        var isAuthenticated = uiModel.isAuthenticated
        var snackbarMessage = uiModel.snackbarMessage

        fun build(): AuthenticationState {
            return AuthenticationState(
                isLoading,
                isFailure,
                isRegistered,
                isAuthenticated,
                snackbarMessage
            )
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Jan*_*ína 5

您正在执行这样的更新:

_uiState.value.build {
    isLoading = true
}
Run Code Online (Sandbox Code Playgroud)

此代码获取当前值uiState并在其上调用构建函数。Build 函数创建 的新实例AuthenticationState但您不对该新实例执行任何操作,并且它会被丢弃。您必须将此新实例设置为 StateFlow 的新值,如下所示:

_uiState.value = _uiState.value.build {}
// or this:
_uiState.update { current -> current.build {} }
Run Code Online (Sandbox Code Playgroud)