在 Android Compose 视图模型中使用 State<Boolean> 是一种好方法吗?

Hel*_*oCW 3 kotlin android-jetpack-compose

我已经阅读了一些学习 Compose 的示例代码。

我发现很多示例项目都使用代码A创建StateFlowin视图模型,然后将其转换为Statein@Composable函数,UI会在drawerOpen更改时自动更新。

1:我认为Code B和Code C都可以做同样的事情,对吧?为什么很多项目很少使用它们?

2:代码A是个好方法吗?

3:我不需要添加rememberSaveable变量drawerOpen@Composable fun myRoute(...)因为视图模型将存储数据,对吧?

代码A

class MainViewModel : ViewModel() {
    private val _drawerShouldBeOpened = MutableStateFlow(false)
    val drawerShouldBeOpened: StateFlow<Boolean> = _drawerShouldBeOpened  
    ...    
}

@Composable
fun myRoute(
    val drawerOpen by MainViewModel.drawerShouldBeOpened.collectAsState()  //Do I need add  rememberSaveable ?
    ...
}
Run Code Online (Sandbox Code Playgroud)

代码B

class MainViewModel : ViewModel() {
    private val _drawerShouldBeOpened = mutableStateOf(false)
    val drawerShouldBeOpened: State<Boolean> = _drawerShouldBeOpened
    ...
}

@Composable
fun myRoute(
   val drawerOpen = MainViewModel.drawerShouldBeOpened  //Do I need add  rememberSaveable ?
   ...
}
Run Code Online (Sandbox Code Playgroud)

代码C

class MainViewModel : ViewModel() {
   private var _drawerShouldBeOpened = false
   val drawerShouldBeOpened: Boolean = _drawerShouldBeOpened
   ...

}

@Composable
fun myRoute(
   val drawerOpen = rememberSaveable { mutableStateOf(MainViewModel.drawerShouldBeOpened)) //Can I remove rememberSaveable ?
   
}
Run Code Online (Sandbox Code Playgroud)

Abh*_*bhi 7

这里有多个问题。
让我回答一切可能的问题。

1. 你应该在哪里使用remember/ rememberSaveable?(代码 A、B 或 C)

仅在代码 C 中需要它。
(在代码A和B中使用也没有问题,但没有优势)

原因
在代码 A 和 B 中 - 状态保存在视图模型中。因此,该值在重组后仍然存在。
但在代码 C 中,状态是在可组合项内部创建和维护的。因此,remember需要使值能够在重组中幸存下来。

文档中的更多详细信息

2. 为什么Code C用得不多?

只要状态(而不是值)发生变化,就会发生可组合重组。

下面给出了一个简单的例子来证明这一点。

class ToggleViewModel : ViewModel() {
    private val _enabledStateFlow = MutableStateFlow(false)
    val enabledStateFlow: StateFlow<Boolean> = _enabledStateFlow

    private val _enabledState = mutableStateOf(false)
    val enabledState: State<Boolean> = _enabledState

    private var _enabled = false
    val enabled: Boolean = _enabled

    fun setEnabledStateFlow(isEnabled: Boolean) {
        _enabledStateFlow.value = isEnabled
    }

    fun setEnabledState(isEnabled: Boolean) {
        _enabledState.value = isEnabled
    }

    fun setEnabled(isEnabled: Boolean) {
        _enabled = isEnabled
    }
}

@Composable
fun BooleanToggle(
    viewmodel: ToggleViewModel = ToggleViewModel(),
) {
    val enabledStateFlow by viewmodel.enabledStateFlow.collectAsState()
    val enabledState by viewmodel.enabledState
    val enabled by rememberSaveable {
        mutableStateOf(viewmodel.enabled)
    }

    Column {
        Row(
            horizontalArrangement = Arrangement.SpaceBetween,
            verticalAlignment = Alignment.CenterVertically,
            modifier = Modifier
                .fillMaxWidth()
                .padding(16.dp),
        ) {
            Text(text = if (enabledStateFlow) {
                "Enabled"
            } else {
                "Disabled"
            })
            Button(onClick = { viewmodel.setEnabledStateFlow(!enabledStateFlow) }) {
                Text("Toggle State Flow")
            }
        }

        Row(
            horizontalArrangement = Arrangement.SpaceBetween,
            verticalAlignment = Alignment.CenterVertically,
            modifier = Modifier
                .fillMaxWidth()
                .padding(16.dp),
        ) {
            Text(text = if (enabledState) {
                "Enabled"
            } else {
                "Disabled"
            })
            Button(onClick = { viewmodel.setEnabledState(!enabledState) }) {
                Text("Toggle State")
            }
        }

        Row(
            horizontalArrangement = Arrangement.SpaceBetween,
            verticalAlignment = Alignment.CenterVertically,
            modifier = Modifier
                .fillMaxWidth()
                .padding(16.dp),
        ) {
            Text(text = if (enabled) {
                "Enabled"
            } else {
                "Disabled"
            })
            Button(onClick = { viewmodel.setEnabled(!enabled) }) {
                Text("Toggle Value")
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以看到,单击按钮后第三个文本不会更新。
原因是可组合项内的可变状态是使用视图模型数据中的初始值创建的。但对该数据的进一步更新不会反映在可组合项中。

为了获取更新,我们必须使用反应性数据,例如FlowLiveDataState及其变体。

3. 使用StateFlowvs State

文档中,您可以看到 compose 支持Flow,LiveDataRxJava

collectAsState()您可以在我们使用for的用法中看到StateFlow。该方法转换StateFlowState. 所以两者都可以使用。

Flow如果超出的层ViewModel(如存储库)是数据源并且它们使用数据类型,则使用Flow
其他MutableState应该没问题。