Kotlin - StateFlow 不向其收集器发出更新

The*_*awd 14 android kotlin kotlin-coroutines android-jetpack-compose kotlin-flow

我的应用程序中有一个 UserStateModel(数据类)类型的 StateFlow。

private val _userStateFlow: MutableStateFlow<UserStateModel?> = MutableStateFlow(UserStateModel())
val userStateFlow: StateFlow<UserStateModel?> = _userStateFlow
Run Code Online (Sandbox Code Playgroud)

这是用户状态模型

data class UserStateModel(
    val uid: String? = null,
    val username: String? = null,
    val profileImageUrl: String? = null,
    var isLoggedIn: Boolean = false,
    val isPremiumUser: Boolean = false,
    val posts: List<Post>? = listOf()
) 
Run Code Online (Sandbox Code Playgroud)

当我使用新的用户名更新 StateFlow 时,它会将更改发送给收集器并更新 UI。但是当我更改帖子内的属性时:列表?列表它不会发出更改。当我更改列表的大小时,它会更改,而当我更改索引 0 处的帖子的 name 属性时,它不会更改。如何检测 Data 类的子属性的更改?

现在我使用一个丑陋的解决方法,我补充道

val updateErrorWorkaround: Int = 0 
Run Code Online (Sandbox Code Playgroud)

到 UserStateModel 数据类并将其加一以便收集器收到通知

PS我正在使用 MVVM + Clean Architecture 和 Jeptack Compose

编辑 这是我的帖子模型:

data class Post(
    val id: Int,
    val name: String, 
    val tags: MutableList<Tag>? = null

)
Run Code Online (Sandbox Code Playgroud)

以下是我更新 MutableList 的方法:

val posts = userStateFlow.value?.posts
posts.get(index).tags?.add(myNewTag)
_userStateFlow.value = userStateFlow.value?.copy(posts = posts)
Run Code Online (Sandbox Code Playgroud)

这些更改不会发送给收集器

bro*_*oot 56

StateFlow仅当它检测到值的更改时才发出,它会忽略用相同数据替换该值。为此,它将前一个值与新值进行比较。因此,我们不应该修改已经提供给 的数据StateFlow,因为它将无法检测到更改。

例如,我们设置valueUser(name=John). name然后,我们通过将其修改为来改变同一个用户对象James,并将其设置value为这个“新”用户对象。StateFlow将“new”User(name=James)与其存储的值进行比较,该值现在也是User(name=James),因此它看不到任何变化。

在您的示例中,您创建了 的副本UserStateModel,但在内部重复使用相同的对象并对它们进行变异。在这种情况下,您添加了一个新项目,tags并且此更改也会影响旧项目UserStateModel,因此StateFlow不会检测到该更改。

要解决该问题,您需要复制所有已更改的数据,并且不要就地更改任何内容。使所有数据不可变会更安全,因此,val这样List您就被迫制作副本。我更改tagsval tags: List<Tag> = listOf(),那么您的代码可能如下所示:

val posts = userStateFlow.value?.posts!!.toMutableList()
posts[index] = posts[index].copy(tags = posts[index].tags + myNewTag)
userStateFlow.value = userStateFlow.value?.copy(posts = posts)
Run Code Online (Sandbox Code Playgroud)

这里我们不仅创建了一个副本UserStateModel。我们还复制posts列表、Post我们修改的列表以及标签列表。

或者,如果 的这种行为对StateFlow您来说更烦人而不是有帮助,您可以使用SharedFlow它不比较值,而只是发出。