【问题标题】:Flow doesn't update ComposableFlow 不会更新 Composable
【发布时间】:2021-11-28 18:12:34
【问题描述】:

我遇到了以下问题:

有一个注册屏幕,上面有几个输入字段。当用户输入内容时,该值将传递给 ViewModel,设置为屏幕状态并通过 StateFlow 传递回屏幕。从可组合中,我正在观察这个 StateFlow。问题是 Composable 在将新值发送到 Flow 后并没有失效。

这是 ViewModel 代码:

class RegistrationViewModel : BaseViewModel() {

    private val screenData = CreateAccountRegistrationScreenData.init()

    private val _screenDataFlow = MutableStateFlow(screenData)
    internal val screenDataFlow = _screenDataFlow.asStateFlow()

    internal fun updateFirstName(name: String) {
        screenData.firstName = name
        updateScreenData()
    }

    private fun updateScreenData() {
        viewModelScope.launch {
            _screenDataFlow.emit(screenData.copy())
        }
        println()

    }

}

这是可组合的代码:

@Composable
fun RegistrationScreen(navController: NavController, stepName: String) {
    val focusManager = LocalFocusManager.current

    val viewModel: RegistrationViewModel by rememberInstance()

    val screenData by viewModel.screenDataFlow.collectAsState()

    Scaffold {
        ConstraintLayout(
            modifier = Modifier
                .fillMaxSize()
                .pointerInput(Unit) {
                    detectTapGestures(onTap = {
                        focusManager.clearFocus()
                    })
                },
        ) {
            val (
                textTopLabel,
                textBottomLabel,
                tfFirstName,
                tfLastName,
                tfEmail,
                tfPassword,
                btnNext
            ) = createRefs()

            ...
            RegistrationOutlinedTextField(
                value = screenData.firstName ?: "",
                constraintAsModifier = {
                    constrainAs(tfFirstName) {
                        top.linkTo(textBottomLabel.bottom, margin = Padding30)
                        start.linkTo(parent.start)
                        end.linkTo(parent.end)
                    }
                },
                label = { Text("First Name", style = PoppinsNormalStyle14) },
                leadingIcon = {
                    Image(
                        painter = painterResource(id = R.drawable.ic_user_login),
                        contentDescription = null
                    )
                },
                onValueChange = { viewModel.updateFirstName(it) }
            )
    }
}

提前感谢您的帮助

【问题讨论】:

    标签: android android-jetpack-compose kotlin-stateflow


    【解决方案1】:

    您的问题是您正在改变您的状态,因此流程中使用的等号始终返回 true。

    改变这个

    internal fun updateFirstName(name: String) {
        screenData.firstName = name
        updateScreenData()
    }
    
    private fun updateScreenData() {
        viewModelScope.launch {
            _screenDataFlow.emit(screenData.copy())
        }
        println()
    
    }
    

    internal fun updateFirstName(name: String) {
        viewModelScope.launch {
            _screenDataFlow.emit(screenData.copy(firstName = name))
        }
    }
    

    【讨论】:

    • 好像copy方法应该返回新的实例,而且它的hashcode和状态实例不一样,不是吗?
    • 当您改变状态时,您正在改变流程中的实例。所以当你发出一个副本时,它已经是一样的了。
    【解决方案2】:

    在您的情况下,您的 MutableStateFlow 包含指向可变值的链接,这就是为什么当您传递 hashcode(根据所有字段值计算)相同的值时,流程不会更新该值。

    查看Why is immutability important in functional programming?

    data class 是一个很好用的工具,它会为你提供开箱即用的所有copy,但你应该使用var 发出并且只在你的字段中使用val 以避免错误。

    此外,使用MutableStateFlow,您始终可以访问value,因此您无需将其存储在单独的变量中:

    class RegistrationViewModel : BaseViewModel() {
    
        private val _screenDataFlow = MutableStateFlow(CreateAccountRegistrationScreenData())
        internal val screenDataFlow = _screenDataFlow.asStateFlow()
    
        internal fun updateFirstName(name: String) {
            _screenDataFlow.update { screenData ->
                screenData.copy(firstName = name)
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-04-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多