androidandroid-jetpack-composeandroid-paging-3

First Header Item inserted by using PagingData<T>.insertSeparators disappear when navigate to other page and back


Hello? I am writing a simple memo app.

The app showing list of memo using paging 3 Library and LazyColumn from Jetpack Compose.

To show date of a group of memos, I am using PagingData<T>.insertSeparators like below.

private fun insertDateSeparators(listThanksRecordUiModels: PagingData<ListThanksRecordUiState.ThanksRecordItemWithImages>) =
    listThanksRecordUiModels.insertSeparators { before: ListThanksRecordUiState.ThanksRecordItemWithImages?, after: ListThanksRecordUiState.ThanksRecordItemWithImages? ->
        createDateHeaderSeparatorsIfNeedTo(after, before)
    }

private fun createDateHeaderSeparatorsIfNeedTo(
    after: ListThanksRecordUiState.ThanksRecordItemWithImages?,
    before: ListThanksRecordUiState.ThanksRecordItemWithImages?
): ListThanksRecordUiState.DateHeaderItem? {
    after ?: return null
    before ?: return ListThanksRecordUiState.DateHeaderItem(after.thanksRecordWithImages.thanksRecord.date)

    return if (before.thanksRecordWithImages.thanksRecord.date != after.thanksRecordWithImages.thanksRecord.date) {
        ListThanksRecordUiState.DateHeaderItem(after.thanksRecordWithImages.thanksRecord.date)
    } else {
        null
    }
}

It works fine, but when I navigate to a detail screen of memo and pop back to list screen, first date header item disappeared life below screen record.

enter image description here

Is there a something I missed?

Full ViewModel Code

@HiltViewModel
class ListThanksViewModel @Inject constructor(
    getThanksRecordWitImagesPagingDataFlow: GetThanksRecordWithImagesPagingDataFlowUseCase,
    @IoDispatcher private val ioDispatcher: CoroutineDispatcher
) : ViewModel() {

    private val _thanksRecordsPagingData = MutableStateFlow<PagingData<ListThanksRecordUiState>>(PagingData.empty())
    val thanksRecordsPagingData: StateFlow<PagingData<ListThanksRecordUiState>> = _thanksRecordsPagingData

    init {
        viewModelScope.launch {
            getThanksRecordWitImagesPagingDataFlow()
                .cachedIn(viewModelScope + ioDispatcher)
                .collectLatest { pagingData ->
                    _thanksRecordsPagingData.update { insertDateSeparators(mapToUiModels(pagingData)) }
                }
        }
    }

    private fun mapToUiModels(pagingData: PagingData<ThanksRecordWithImages>) =
        pagingData.map { thanksRecord ->
            ListThanksRecordUiState.ThanksRecordItemWithImages(thanksRecord)
        }

    private fun insertDateSeparators(listThanksRecordUiModels: PagingData<ListThanksRecordUiState.ThanksRecordItemWithImages>) =
        listThanksRecordUiModels.insertSeparators { before: ListThanksRecordUiState.ThanksRecordItemWithImages?, after: ListThanksRecordUiState.ThanksRecordItemWithImages? ->
            createDateHeaderSeparatorsIfNeedTo(after, before)
        }

    private fun createDateHeaderSeparatorsIfNeedTo(
        after: ListThanksRecordUiState.ThanksRecordItemWithImages?,
        before: ListThanksRecordUiState.ThanksRecordItemWithImages?
    ): ListThanksRecordUiState.DateHeaderItem? {
        after ?: return null
        before ?: return ListThanksRecordUiState.DateHeaderItem(after.thanksRecordWithImages.thanksRecord.date)

        return if (before.thanksRecordWithImages.thanksRecord.date != after.thanksRecordWithImages.thanksRecord.date) {
            ListThanksRecordUiState.DateHeaderItem(after.thanksRecordWithImages.thanksRecord.date)
        } else {
            null
        }
    }
}

Full code repository : https://github.com/ChanJun-Park/SeizeTheDay/blob/master/app/src/main/java/com/jingom/seizetheday/presentation/list/ListThanksViewModel.kt


Solution

  • Why do you collect the paging flow in your ViewModel and forward the values to MutableStateFlow? That's wrong. Do it like this:

    class ListThanksViewModel {
        val thanksRecordsPagingData: Flow<PagingData<ListThanksRecordUiState>> =
            getThanksRecordWitImagesPagingDataFlow()
                .map { pagingData -> mapToUiModels(pagingData) }
                .cachedIn(viewModelScope)
                .map { pagingData -> insertDateSeparators(pagingData) }
    }
    

    and collect this from compose with collectAsLazyPagingItems(). Not sure if it will solve your problem, but it's good to get the basics right first. Also: