标签: kotlin-stateflow

填充并发出 StateFlow 列表

我想使用 StateFlow。但目前,我找不到任何可以帮助我的讲座。

我面临一个问题:首先,我有一个包含字符串列表的单例,我想要一些“容易”理解的东西,即使它不是现在的目标目的。目的是用字符串填充并发出列表(稍后它将是一个复杂的对象)。

class CustomStateFlow() {
    private val _custom = MutableStateFlow(emptyList<String>())
    val custom: StateFlow<List<String>> = _custom

    fun addString(string: String) {
        val tempList = _custom.value.toMutableList()
        tempList.add(string)
        _custom.value = tempList
}
Run Code Online (Sandbox Code Playgroud)

这似乎有效,但我不喜欢临时列表...没有,我无法触发片段中自定义的“收集”。

有没有办法在不使用 tempList 的情况下实现这一目标?

感谢您

android kotlin kotlin-stateflow

9
推荐指数
1
解决办法
7917
查看次数

StateFlow最后一个值在ui中再次收集

最近,我一直在使用 StateFlow、SharedFlow 和 Channels API,但在尝试将代码从 LiveData 迁移到表示层中的 StateFlow 时,我遇到了一个常见用例。

我面临的问题是,当我发出数据并将其收集到 viewModel 中时,我可以将值设置为 mutableStateFlow,当它最终到达片段时,它会使用 Toast 显示一些信息性消息,让用户知道是否发生错误发生了或者一切都很顺利。接下来,有一个按钮可以导航到另一个片段,但是如果我返回到已经有失败意图结果的上一个屏幕,它会再次显示 Toast。这正是我想要弄清楚的。如果我已经收集了结果并向用户显示了消息,我不想继续这样做。如果我导航到另一个屏幕并返回(当应用程序从后台返回时也会发生这种情况,它会再次收集最后一个值)。LiveData 没有发生这个问题,我只是做了完全相同的事情,公开来自存储库的流并通过 ViewModel 中的 LiveData 收集。

代码:

class SignInViewModel @Inject constructor(
    private val doSignIn: SigninUseCase
) : ViewModel(){

    private val _userResult = MutableStateFlow<Result<String>?>(null)
    val userResult: StateFlow<Result<String>?> = _userResult.stateIn(viewModelScope, SharingStarted.Lazily, null) //Lazily since it's just one shot operation

    fun authenticate(email: String, password: String) {
        viewModelScope.launch {
            doSignIn(LoginParams(email, password)).collect { result ->
                Timber.e("I just received this $result in viewmodel")
                _userResult.value = result
            }
        }
    }
    
}
Run Code Online (Sandbox Code Playgroud)

然后在我的片段中: …

android kotlin kotlin-stateflow kotlin-sharedflow

9
推荐指数
2
解决办法
6668
查看次数

Flow 不更新可组合项

我遇到了以下问题:

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

这是视图模型代码:

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()

    }

}
Run Code Online (Sandbox Code Playgroud)

这是可组合的代码:

@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() …
Run Code Online (Sandbox Code Playgroud)

android android-jetpack-compose kotlin-stateflow

7
推荐指数
1
解决办法
3934
查看次数

将两个 kotlin 流合并为一个流,该流发出两个原始流的最新值?

如果我们有两个这样定义的流:

val someflow = flow {
    emit("something")
}
Run Code Online (Sandbox Code Playgroud)

另一个流程定义如下:

val stateFlow = MutableStateFlow("some value")
Run Code Online (Sandbox Code Playgroud)

是否可以将两个流组合成一个流,该流仅发出someflowor发出的最后一个值stateFlow

这个想法是,stateFlow可能会在未来的某个时刻发出一个值,但在那之前我只想要someflow最后发出的任何值。在“组合”流程中,我想只获取 发出的第一个值someflow,但随后能够观察 上的其余更新stateFlow

看起来这可以通过组合函数来完成,但我只想发出两个流之间的最新发出值,我不关心一个流的最后一个值是什么。

coroutine kotlin kotlin-coroutines kotlin-flow kotlin-stateflow

7
推荐指数
1
解决办法
8271
查看次数

Android LiveData/StateFlow 列表项属性更新问题

所以我正在更新我的RecylerView内容StateFlow<List>如下:

我的数据类:

data class Student(val name: String, var isSelected: Boolean)
Run Code Online (Sandbox Code Playgroud)

我的视图模型逻辑:

fun updateStudentsOnSelectionChanged(targetStudent: Student) {
    val targetIndex = _students.value.indexOf(targetStudent)
    val isSelected = !targetStudent.isSelected

    _students.value[targetIndex].isSelected = isSelected        //<- doesn't work
} 
Run Code Online (Sandbox Code Playgroud)

问题: UI没变,但isSelected内部_student变了,这是怎么回事?(和...一样LiveData

android kotlin android-livedata kotlin-flow kotlin-stateflow

7
推荐指数
1
解决办法
3276
查看次数

使用 StateFlow 和协程的单元测试 viewModel

Kotlin 1.4.21
Run Code Online (Sandbox Code Playgroud)

我有一个非常简单的 ViewModel,它使用协程和 stateFlow。但是,单元测试将失败,因为 stateFlow 似乎没有更新。

我认为这是因为测试将在stateFlow更新之前完成。

expected not to be empty

这是我正在测试的 ViewModel

class TrendingSearchViewModel @Inject constructor(
    private val loadTrendingSearchUseCase: LoadTrendingSearchUseCase,
    private val coroutineDispatcher: CoroutineDispatcherProvider
) : ViewModel() {

    private val trendingSearchMutableStateFlow = MutableStateFlow<List<String>>(emptyList())
    val trendingSearchStateFlow = trendingSearchMutableStateFlow.asStateFlow()

    fun getTrendingSearch() {
        viewModelScope.launch(coroutineDispatcher.io()) {
            try {
                trendingSearchMutableStateFlow.value = loadTrendingSearchUseCase.execute()
            } catch (exception: Exception) {
                Timber.e(exception, "trending ${exception.localizedMessage}")
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的实际测试课程,我尝试了不同的方法来使其正常工作

class TrendingSearchViewModelTest {
    private val loadTrendingSearchUseCase: LoadTrendingSearchUseCase = mock()
    private val coroutineDispatcherProvider = CoroutineDispatcherProviderImp()
    private …
Run Code Online (Sandbox Code Playgroud)

android unit-testing kotlin kotlin-coroutines kotlin-stateflow

7
推荐指数
1
解决办法
5071
查看次数

如何将 DataStore 与 StateFlow 和 Jetpack Compose 结合使用?

我尝试让用户选择是否使用 UI 模式“浅色”、“深色”或“系统”设置。我想将选择保存为数据存储。

用户选择的下拉菜单未从数据存储加载值。加载屏幕时,它始终显示 stateIn() 的初始值。

设置管理器:

val Context.dataStoreUiSettings: DataStore<Preferences> by preferencesDataStore(name = DATA_STORE_UI_NAME)

object PreferencesKeys {
    val UI_MODE: Preferences.Key<Int> = intPreferencesKey("ui_mode")
}

class SettingsManager @Inject constructor(private val context: Context) { //private val dataStore: DataStore<Preferences>
    private val TAG: String = "UserPreferencesRepo"

    // Configuration.UI_MODE_NIGHT_UNDEFINED, Configuration.UI_MODE_NIGHT_YES, Configuration.UI_MODE_NIGHT_NO
    suspend fun setUiMode(uiMode: Int) {
        context.dataStoreUiSettings.edit { preferences ->
            preferences[UI_MODE] = uiMode
        }
    }
    fun getUiMode(key: Preferences.Key<Int>, default: Int): Flow<Int> {
        return context.dataStoreUiSettings.data
            .catch { exception ->
                if (exception is IOException) {
                    Timber.i("Error reading preferences: …
Run Code Online (Sandbox Code Playgroud)

kotlin android-jetpack-compose android-jetpack-datastore kotlin-stateflow

7
推荐指数
1
解决办法
3977
查看次数

难道collectAsStateWithLifecycle只适用于冷流,对热流没有帮助(例如stateFlow)?

最近,自 android 生命周期库版本 2.6.0-alpha01 以来,我们有了一个新的 API,即

\n
collectAsStateWithLifecycle(...)\n
Run Code Online (Sandbox Code Playgroud)\n

这是 Google Developer 在这篇文章中提倡的

\n
\n

如果您\xe2\x80\x99 使用 Jetpack Compose 构建 Android 应用程序,请使用\ncollectAsStateWithLifecycle可组合函数(而不是\n collectAsState

\n
\n

我尝试一下,对于流动(冷流)例如

\n
    val counter = flow {\n        var value = 0\n        while (true) {\n           emit(value++)\n           delay(1000)\n        }\n    }\n
Run Code Online (Sandbox Code Playgroud)\n

拥有它是有用的

\n
flow.collectAsStateWithLifecycle(0)\n
Run Code Online (Sandbox Code Playgroud)\n

但是如果我们有一个像 mutableStateFlow 这样的热流

\n
val stateFlow = MutableStateFlow(0)\n
Run Code Online (Sandbox Code Playgroud)\n

拥有似乎没什么用

\n
stateFlow.collectAsStateWithLifecycle(0)\n
Run Code Online (Sandbox Code Playgroud)\n

鉴于它不会阻止任何排放。

\n

我说collectAsStateWithLifecycle只对冷流有用,对热流无效,这样说对吗?

\n

如果我错了,你能给我举一个collectAsStateWithLifecycle对热流也有用的例子吗?

\n

android android-lifecycle kotlin kotlin-flow kotlin-stateflow

7
推荐指数
1
解决办法
5739
查看次数

如何在 MutableStateFlow 中初始化密封类

我有一个这样的密封类

sealed class LoadState {
    class Loading : LoadState()
    class Success : LoadState()
    class Fail : LoadState()
}
Run Code Online (Sandbox Code Playgroud)

我将密封类与 LiveData 一起使用,它有效

open class BaseViewModel: ViewModel() {
//    val loadState by lazy {
//        MutableLiveData<LoadState>()
//    }
    val loadState by lazy {
        MutableStateFlow(LoadState())
    }
}
Run Code Online (Sandbox Code Playgroud)

但是当我将 MutableLiveData 更改为 MutableStateFlow 时,我收到这样的警告

Sealed types cannot be instantiated
Run Code Online (Sandbox Code Playgroud)

那么,如何在 MutableStateFlow 中使用密封类呢?

android sealed-class kotlin-flow kotlin-stateflow

6
推荐指数
2
解决办法
4181
查看次数

在数据绑定中未观察到 Stateflow 值

我有一个状态流,它为我提供来自 ViewModel 的可变状态流的值,我想做的是,我想根据按钮单击显示隐藏 Web 视图。当值为 true 时,我想显示 Web 视图,当我想隐藏它时,我将其值更改为 false。这些值已正确更新,但未反映内部数据绑定。这是我的视图模型

class ArticleDetailsViewModel : ViewModel() {
    private val _isWebViewShowing = MutableStateFlow(false)
    val isWebViewShowing: StateFlow<Boolean>
        get() = _isWebViewShowing

    fun onReadMoreClicked() {

        _isWebViewShowing.value = true
    }

    fun changeWebViewState() {
        _isWebViewShowing.value = false
    }}
Run Code Online (Sandbox Code Playgroud)

这是我正在进行比较的 XML

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
            name="article"
            type="io.infinity.newsapp.model.domain.ArticleDomainModel" />
        <variable
            name="viewModel"
            type="io.infinity.newsapp.viewmodels.ArticleDetailsViewModel" />
        <import type="android.view.View"/>
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <include
            android:id="@+id/toolbar"
            layout="@layout/toolbar_generic"
            />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:visibility="@{viewModel.isWebViewShowing().value ?  View.INVISIBLE :View.VISIBLE  }"
            android:orientation="vertical"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="@+id/toolbar"
            app:layout_constraintStart_toStartOf="@+id/toolbar" …
Run Code Online (Sandbox Code Playgroud)

android kotlin android-livedata kotlin-coroutines kotlin-stateflow

6
推荐指数
1
解决办法
1876
查看次数