How to use paging3 library in jetpack compose

180 views Asked by At

I'm trying to use the paging library for my project but it doesn't seem to work. I'll leave my classes below to see if anyone can help me.

My FrutasViewModel:

class FrutasViewModel : ViewModel() {
    private lateinit var content: Flow<PagingData<CategoryProductQueryRequest>>
    var stateStatus =
        MutableStateFlow<StateStatus<Flow<PagingData<CategoryProductQueryRequest>>>>(StateStatus.Start)
    val managedGrpc = ManagedGrpc()
    private var stubCategoryProductQuery: CategoryProductQueryGrpc.CategoryProductQueryStub

    init {
        managedGrpc.inicializar("categoryproduct")
        stubCategoryProductQuery = CategoryProductQueryGrpc.newStub(managedGrpc.canal)

    }

    fun openChannel() {
        if (managedGrpc.canal.isShutdown) {
            managedGrpc.inicializar("categoryproduct")
            stubCategoryProductQuery = CategoryProductQueryGrpc.newStub(managedGrpc.canal)
        }
    }

    fun getFrutas(id: Int?): Flow<PagingData<CategoryProductQueryRequest>> {
        stateStatus.value = StateStatus.Loading
        try {

            content = getFrutasPage(id).cachedIn(viewModelScope)
            stateStatus.value = StateStatus.Success(content)

        } catch (e: Exception) {
            stateStatus.value = StateStatus.Failure(errorMessage = e.localizedMessage!!)
        }
        return content
    }


    private fun getFrutasPage(id: Int?): Flow<PagingData<CategoryProductQueryRequest>> {
        return Pager(
            config = PagingConfig(
                pageSize = 10,
                enablePlaceholders = false
            ),
            pagingSourceFactory = {
                ShoppingDataSource(
                    id = id,
                    stub = stubCategoryProductQuery,
                    managedGrpc = managedGrpc
                )
            }
        ).flow
    }
}

My ShoppingDataSource:

class ShoppingDataSource(
    private val id:Int?,
    private var stub: CategoryProductQueryGrpc.CategoryProductQueryStub,
    private var managedGrpc : ManagedGrpc
) : PagingSource<Int, CategoryProductQueryRequest>() {
    private var itens: CategoryQueryResponse? = null
    private var requestObserver: StreamObserver<CategoryQueryRequest>?=null
    private val responseObserver = object : StreamObserver<CategoryQueryResponse> {
        override fun onNext(response: CategoryQueryResponse) {
            itens =CategoryQueryResponse.newBuilder()
                .setCount(response.count)
                .setNextPage(response.nextPage)
                .setPrevPage(response.prevPage)
                .addAllResults(response.resultsList)
                .setPages(response.pages).build()

        }

        override fun onError(t: Throwable?) {
            val status: Status = Status.fromThrowable(t)
            Log.e("Failed",status.description!!)
            finishLatch.countDown()
        }

        override fun onCompleted() {
            finishLatch.countDown()
        }
    }
    val finishLatch = CountDownLatch(1)

    // Função para fechar o canal gRPC
    private fun closeCanal() {
        if (!managedGrpc.canal.isShutdown) {
            managedGrpc.canal.shutdown()
        }
    }

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, CategoryProductQueryRequest> {

        // Retrofit calls that return the body type throw either IOException for network
        // failures, or HttpException for any non-2xx HTTP status codes. This code reports all
        // errors to the UI, but you can inspect/wrap the exceptions to provide more context.
        return try {
            // Key may be null during a refresh, if no explicit key is passed into Pager
            // construction. Use 0 as default, because our API is indexed started at index 0
            val pageNumber = params.key ?: 0

            // Suspending network load via Retrofit. This doesn't need to be wrapped in a
            // withContext(Dispatcher.IO) { ... } block since Retrofit's Coroutine
            // CallAdapter dispatches on a worker thread.

            requestObserver =stub.getCategoryProduct(responseObserver)
            try {
               val request= CategoryQueryRequest.newBuilder()
                        .setData(id!!.toLong())
                        .setPageNum(pageNumber)
                        .build()
                requestObserver?.onNext(request)

                withContext(Dispatchers.IO) {
                    finishLatch.await(1000, TimeUnit.MILLISECONDS)
                }

            } catch (e: RuntimeException) {
                requestObserver?.onError(e)
                Log.e("ERRO",e.message!!)
            }

            // Since 0 is the lowest page number, return null to signify no more pages should
            // be loaded before it.
            val prevKey = if (pageNumber > 1) pageNumber - 1 else null

            // This API defines that it's out of data when a page returns empty. When out of
            // data, we return `null` to signify no more pages should be loaded
            val nextKey = if (pageNumber+1>itens!!.pages) null else pageNumber+1

            if(nextKey==null){
                requestObserver?.onCompleted()
                closeCanal()
            }

            LoadResult.Page(
                    data = itens!!.resultsList,
                    prevKey = prevKey,
                    nextKey = nextKey
            )


        } catch (e: IOException) {
            closeCanal()
            LoadResult.Error(e)
        } catch (e: HttpException) {
            if (Build.VERSION.SDK_INT >= 34) {
                closeCanal()
                LoadResult.Error(e)
            } else {
                closeCanal()
               TODO("VERSION.SDK_INT < 34")
            }
        }
    }

    override fun getRefreshKey(state: PagingState<Int, CategoryProductQueryRequest>): Int? {
        return state.anchorPosition?.let {
            state.closestPageToPosition(it)?.prevKey?.plus(1)
                ?: state.closestPageToPosition(it)?.nextKey?.minus(1)
        }
    }


}

My class StateStatus:

sealed class StateStatus<out T> where T : Any? {
    object Start : StateStatus<Nothing>()

    object Loading : StateStatus<Nothing>()

    data class Success<T>(val data: T) : StateStatus<T>()

    data class Failure(val errorMessage: String) : StateStatus<Nothing>()
}

And finally my code calls the viewmodel:

LaunchedEffect(Unit){
    withContext(Dispatchers.IO) {
        // Operações de entrada/saída (ex: chamadas de rede) aqui
            viewModel.getFrutas(id, without)
    }

}

val states by viewModel.stateStatus.collectAsState()

when (states) {
    is StateStatus.Start -> {

    }
    StateStatus.Loading -> {
        helpers.LoadingComponent()
    }
    is StateStatus.Failure -> {
        Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
            Text(text = stringResource(id = R.string.error),fontSize = 16.sp)
        }
    }
    is StateStatus.Success -> {
        list =  (states as StateStatus.Success).data.collectAsLazyPagingItems()
        images = imgs
        idFrutas = id
        modelFrutas=viewModel

        
            LazyColumn(
                state = rememberLazyListState(),

                ) {
                items(
                    count = list.itemCount,
                    key = list.itemKey(),
                    contentType = list.itemContentType()
                )
                { index ->
                    val item = list[index]


                    if (item != null) {

                        var visible by rememberSaveable(index) {
                            mutableStateOf(true)
                        }

                        ShowCategories(item)
                    }
                }
            }
        }

    }

}

Version:

implementation("androidx.paging:paging-compose:3.2.1")
implementation("androidx.paging:paging-runtime-ktx:3.2.1")

When I execute the project and see the list on my cell phone, it slows down, does anyone know how to help me fix it?

0

There are 0 answers