簡體   English   中英

如何在 Jetpack Compose 的 viewModel 內將 DataStore 首選項的 Flow 轉換為 StateFlow

[英]How can I convert DataStore preferences' Flow to StateFlow inside a viewModel in Jetpack Compose

我正在嘗試使用 DataStore 參考為我的應用程序實現“設置”屏幕。 基本上,根據偏好創建一個 url,因此 WebView 可以稍后加載它。 數據已正確保存,設置屏幕中的 UI 會在首選項更改時更新值。

問題是我發現,在嘗試加載更新的 url 時,不會立即讀取首選項(WebView 在更新后加載以前的 url 值)。 所以我的問題是如何將其轉換為該類型以便實時讀取首選項?

以下是一些代碼片段:

用戶偏好數據 class

data class UserPreferences(
    val conexionSegura: Boolean,
    val servidor: String,
    val puerto: String,
    val pagina: String,
    val parametros: String,
    val empresa: String,
    val inicioDirecto: Boolean,
    val usuario: String,
    val password: String,
    val url: String
)

DataStore 存儲庫實現 getPreferences()

class DataStoreRepositoryImpl @Inject constructor(
    private val dataStore: DataStore<Preferences>
) : DataStoreRepository {


    override suspend fun getPreferences() =
        dataStore.data
            .map { preferences ->
                UserPreferences(
                    conexionSegura = preferences[PreferencesKeys.CONEXION_SEGURA] ?: false,
                    servidor = preferences[PreferencesKeys.SERVIDOR] ?: "",
                    puerto = preferences[PreferencesKeys.PUERTO] ?: "",
                    pagina = preferences[PreferencesKeys.PAGINA] ?: "",
                    parametros = preferences[PreferencesKeys.PARAMETROS] ?: "",
                    empresa = preferences[PreferencesKeys.EMPRESA] ?: "",
                    inicioDirecto = preferences[PreferencesKeys.INICIO_DIRECTO] ?: false,
                    usuario = preferences[PreferencesKeys.USUARIO] ?: "",
                    password = preferences[PreferencesKeys.PASSWORD] ?: "",
                    url = preferences[PreferencesKeys.URL] ?: CONTENT_URL
                )
            }
...
...

視圖模型 readPreferences()

 private val _state = mutableStateOf(SettingsState())

    val state: State<SettingsState> = _state

    init {
        readPreferences()
        updateUrl()
    }

    private fun readPreferences() {
        viewModelScope.launch {
            dataStoreRepositoryImpl.getPreferences().collect {
                _state.value = state.value.copy(
                    conexionSegura = it.conexionSegura,
                    servidor = it.servidor,
                    puerto = it.puerto,
                    pagina = it.pagina,
                    parametros = it.parametros,
                    empresa = it.empresa,
                    inicioDirecto = it.inicioDirecto,
                    usuario = it.usuario,
                    password = it.password
                )
            }
        }
    }
...
...

在調查和閱讀了一些內容之后,我意識到 DataStore 發出流,而不是 stateFlows,這在 compose 中是需要的。 我考慮過刪除 UserPreferences class,一次只讀取一個偏好,將其收集為屏幕可組合項中的狀態。 但是,我認為代碼會更清晰,因為我不這樣做 :) 我真的很喜歡使用單獨的 class 來保存首選項值的想法

當您想要將 ViewModel 中的Flow轉換為StateFlow以在視圖中使用時,正確的方法是使用stateIn()方法。

狀態輸入的使用

假設你有以下 class 和接口:

class SettingsState()

sealed interface MyDatastore {
    fun getPreferences(): Flow<SettingsState>
}

在您的 viewModel 中,創建一個將使用 datatStore.getPreferences 方法的 val,並使用 stateIn 將流轉換為 stateFlow

class MyViewModel(
    private val dataStore: MyDatastore
) : ViewModel() {

    val state: StateFlow<SettingsState> = dataStore
        .getPreferences()
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5_000),
            initialValue = SettingsState()
        )
}

在可組合項中收集

要開始獲取首選項,您只需將 stateFlow 收集為 state:

@Composable
fun MyComposable(
    myViewModel: MyViewModel = hiltViewModel()
) {
    
    val state = myViewModel.state.collectAsState()
    
    //...
    
}

優點

如您所見,您不需要在ViewModel中使用init 像 90% 的時間一樣,不需要使用init ViewModel 變得更易於測試,因為您不需要模擬init塊中的所有內容。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM