每当我触发“FavoritePost”或“DeletePost”并且uiState选择了TAB_TWO(收藏夹)时,它要么交换到TAB_ONE,保留在TAB_TWO中,但显示所有帖子而不仅仅是最喜欢的帖子,或者只是正常工作。
这是该屏幕的 ViewModel:
@HiltViewModel
class MainViewModel @Inject constructor(
private val repo: MainRepository
) : ViewModelTemplate<MainState, MainScreenEvents>(MainState()) {
init {
initState()
}
override fun initState() {
d("MainViewModel", "Initialized posts")
viewModelScope.launch {
repo.getPosts()
.distinctUntilChanged()
.collectLatest {
_uiState.emit(
_uiState.value.copy(posts = it)
)
}
}
}
override fun onEvent(event: MainScreenEvents) {
when (event) {
is ChangeTab -> {
viewModelScope.launch {
d("MainViewModel", "Changing to ${event.tabNumber}")
when (event.tabNumber) {
TAB_ONE -> repo.getPosts()
TAB_TWO -> repo.getFavoritedPosts()
}.distinctUntilChanged()
.collect {
_uiState.emit(
_uiState.value.copy(
selectedTab = event.tabNumber,
posts = it
)
)
}
}
}
is DeletePost -> {
viewModelScope.launch {
repo.setDeletedPost(event.post)
if (event.post.isFavorited)
repo.setFavoritePost(event.post)
}
}
is FavoritePost -> {
viewModelScope.launch {
repo.setFavoritePost(event.post)
}
}
}
}
}
视图模型模板:
abstract class ViewModelTemplate<T : States, in R : Events>(initialState: T) : ViewModel() {
protected val _uiState: MutableStateFlow<T> = MutableStateFlow(initialState)
val uiState: StateFlow<T> = _uiState
protected open fun initState() {
d("viewmodel", "I did nothing!")
}
abstract fun onEvent(event: R)
}
活动类:
sealed class Events {
sealed class MainScreenEvents : Events() {
data class FavoritePost(val post: PostsEntity) : MainScreenEvents()
data class DeletePost(val post: PostsEntity) : MainScreenEvents()
data class ChangeTab(val tabNumber: MainScreenTabId) : MainScreenEvents()
}
sealed class DeletedPostsScreenEvents : Events() {
data class CheckPost(val post: DeletedPosts) : DeletedPostsScreenEvents()
data object RestorePosts : DeletedPostsScreenEvents()
data object RestoreAllPosts : DeletedPostsScreenEvents()
}
sealed class PostScreenEvents : Events() {
data class PostEntity(val title: String, val body: String) : PostScreenEvents()
}
}
标签:
enum class MainScreenTabId(val text: String, val icon: ImageVector) {
TAB_ONE("Home", Default.Home),
TAB_TWO("Favorite", Default.Favorite)
}
州:
sealed class States {
data class MainState(
val posts: List<PostsEntity> = emptyList(),
val selectedTab: MainScreenTabId = TAB_ONE
) : States()
data class DeletedPostsState(
val posts: List<DeletedPosts> = emptyList()
) : States()
data object EmptyState : States()
}
存储库:
class MainRepository @Inject constructor(
private val postsService: PostsServiceImplementation,
private val postsDao: PostsDao
) {
init {
CoroutineScope(IO).launch {
postsDao.updateDbFromClient(
postsService.getPosts().map {
it.toEntity()
})
}
}
fun getPosts(): Flow<List<PostsEntity>> {
return postsDao.getAvailablePosts()
}
fun getFavoritedPosts(): Flow<List<PostsEntity>> {
return postsDao.getFavoritedPosts()
}
fun getDeletedPosts(): Flow<List<PostsEntity>> {
return postsDao.getDeletedPosts()
}
suspend fun setFavoritePost(post: PostsEntity) {
postsDao.updatePost(post.copy(isFavorited = !post.isFavorited))
}
suspend fun setDeletedPost(post: PostsEntity) {
postsDao.updatePost(post.copy(isDeleted = !post.isDeleted))
}
suspend fun createPosts(postRequest: PostRequest): PostResponse? {
return postsService.createPosts(postRequest)
}
}
如果有帮助,我会使用 RoomDB,但我真的认为问题在于我如何处理 ViewModel 内的 Flow。
如果您对我的代码有什么需要补充的,请补充,我想学习!
我可以保证问题不在于我如何处理可组合项中的事件。
尝试将 StateFlow 处理集中在单个函数中(没有改变任何内容)
干杯!
您不应该在视图模型中收集流,它们只应该被转换并转换为 StateFlow,然后在您的可组合项中收集。
从
_uiState
中移除 uiState
和 ViewModelTemplate
,并通过以下方式替换 init
中的 initState
块和 MainViewModel
:
private val currentTab: MutableStateFlow<MainScreenTabId> = MutableStateFlow(TAB_ONE)
val uiState: StateFlow<MainState> = currentTab
.flatMapLatest {
when (it) {
TAB_ONE -> repo.getPosts()
TAB_TWO -> repo.getFavoritedPosts()
}
}
.combine(currentTab, ::MainState)
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = MainState(),
)
基础是当前选项卡,它包含在流中,因此其他流操作可以基于它。首先发生的事情是根据当前选项卡将流程切换到正确的存储库流程。然后,流的内容与当前选项卡一起转换为 MainState 对象。然后该流被转换为 StateFlow,以便 UI 可以轻松收集。
切换选项卡时,唯一要做的就是设置
currentTab
,StateFlow(和 UI)就会相应更新。您只需将 ChangeTab
中的 onEvent
分支简化为:
is ChangeTab -> {
currentTab.value = event.tabNumber
}