使用 viewModel 作为 Jetpack Compose 中的单一真实来源

Using viewModel as the single source of truth in jetpack compose

假设我们有一个 viewModel,其内部有一个名为 apiKey 的值。该值的内容以流的形式从 DataStore 接收,然后作为 LiveData 公开。 另一方面,我们有一个名为 SettingsFragment 的 Fragment,我们试图在 TextField 中显示该 apiKey,让用户修改它并立即将其保存在 DataStore 中。 我目前使用的解决方案在下面,但问题是 UI 在对文本进行更改时变得非常滞后和缓慢。 我的问题是,什么是最好的实现方式,并且我们的 apiKey 仍然有单一的真实来源?

class SettingsViewModel() : ViewModel() {

    val apiKey = readOutFromDataStore.asLiveData()

    fun saveApiKey(apiKey: String) {
        viewModelScope.launch(Dispatchers.IO) {
            saveToDataStore("KEY", apiKey)
        }
    }
}

/** SettingsFragment **/
...

@Composable
fun ContentView() {
    var text = mViewModel.apiKey.observeAsState().value?.apiKey ?: ""

    Column() {
        OutlinedTextField(
            label = { Text(text = "API Key") },
            value = text,
            onValueChange = {
                text = it
                mViewModel.saveApiKey(it)
            })
    }
}

不要在每次按键时将 onValueChange 事件中的 TextField 值保存到数据存储中——这几乎肯定会减慢你的速度——尤其是当你使用同一个线程时。使用本地状态变量,仅当用户将焦点移到别处或通过按下某些按钮保存屏幕上的内容时才更新数据存储。您还需要避免将 UI 线程与应该在 IO 线程上的数据存储线程混合。这是一种可能的解决方案:

@Composable
fun ContentViewHandler() {
    ContentView(
        initialText = viewmodel.getApiKey(),
        onTextChange = { text ->
            viewmodel.updateApiKey(text)
        }
    )
}

@Composable
fun ContentView(
    initialText: String,
    onTextChange: (text: String) -> Unit
) {
    var text by remember { mutableStateOf(initialText) }

    Column() {
        OutlinedTextField(
            label = { Text(text = "API Key") },
            value = text,
            onValueChange = {
                text = it
            },
            modifier = Modifier.onFocusChanged {
                onTextChange(text)
            }
        )

        // Or add a button and save the text when clicked.
    }
}