Jetpack 使用 StateFlow 编写、获取和添加更多项目到 LazyColumn

Jetpack Compose, fetch and add more items to LazyColumn using StateFlow

我有一个 LazyColumn 可以呈现项目列表。但是,我现在想获取更多项目以添加到我的惰性列表中。我不想 re-render 已经在 LazyColumn 中呈现的项目,我只想添加新项目。

如何使用 StateFlow 执行此操作?我需要传递 page 字符串来获取下一组项目,但是如何将页面传递给 repository.getContent() 方法?

class FeedViewModel(
    private val resources: Resources,
    private val repository: FeedRepository
) : ViewModel() {

// I need to pass a parameter to `repository.getContent()` to get the next block of items

    private val _uiState: StateFlow<UiState> = repository.getContent()
        .map { content ->
            UiState.Ready(content)
        }.catch { cause ->
            UiState.Error(cause.message ?: resources.getString(R.string.error_generic))
        }.stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(stopTimeoutMillis = SUBSCRIBE_TIMEOUT_FOR_CONFIG_CHANGE),
            initialValue = UiState.Loading
        )
    val uiState: StateFlow<UiState>
        get() = _uiState

在我的 UI 中,我有这段代码来观察流程并呈现 LazyColumn:

    val lifecycleAwareUiStateFlow: Flow<UiState> = remember(viewModel.uiState, lifecycleOwner) {
        viewModel.uiState.flowWithLifecycle(lifecycleOwner.lifecycle, Lifecycle.State.STARTED)
    }

    val uiState: UiState by lifecycleAwareUiStateFlow.collectAsState(initial = UiState.Loading)

@Composable
fun FeedLazyColumn(
    posts: List<Post> = listOf(),
    scrollState: LazyListState
) {

    LazyColumn(
        modifier = Modifier.padding(vertical = 4.dp),
        state = scrollState
    ) {
 
        // how to add more posts???
        items(items = posts) { post ->
            Card(post) 
        }
    }
}

我知道有一个用于 Compose 的分页库,但我正在尝试实现类似的东西,除了用户负责是否加载下一个项目。

这是期望的行为:

我能够通过在发布之前将新帖子添加到旧帖子来解决这个问题。有关相关行,请参阅下面的评论。

private val _content = MutableStateFlow<Content>(Content())
    private val _uiState: StateFlow<UiState> = repository.getContent()
        .mapLatest { content ->
            _content.value = _content.value + content // ADDED THIS
            UiState.Ready(_content.value)
        }.catch { cause ->
            UiState.Error(cause.message ?: resources.getString(R.string.error_generic))
        }.stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(stopTimeoutMillis = SUBSCRIBE_TIMEOUT_FOR_CONFIG_CHANGE),
            initialValue = UiState.Loading
        )

private operator fun Content.plus(content: Content): Content = Content(
    posts = this.posts + content.posts,
    youTubeNextPageToken = content.youTubeNextPageToken
)
class YouTubeDataSource(private val apiService: YouTubeApiService) :
    RemoteDataSource<YouTubeResponse> {

    private val nextPageToken = MutableStateFlow<String?>(null)

    fun setNextPageToken(nextPageToken: String) {
        this.nextPageToken.value = nextPageToken
    }

    override fun getContent(): Flow<YouTubeResponse> = flow {
        // retrigger emit when nextPageToken changes
        nextPageToken.collect {
            emit(apiService.getYouTubeSnippets(it))
        }
    }
}