How to implement pagination in Jetpack compose?
Last updated Nov 10, 2021In this Jetpack compose tutorial we will learn how to implement pagination using Jetpack compose in Android application.
Let's get started
Step 1: Create android application in android studio
Step 2: Follow step for setup Jetpack Compose with Android Studio
To handle big amount of data in app we use Paging Library.
Lets add dependency for paging
implementation 'androidx.paging:paging-compose:1.0.0-alpha14' |
Api we are using is given below:
https://reqres.in/api/users?page=1&per_page=6 |
Response of Api is aslo given below:
{"page":1, "per_page":6, "total":12, "total_pages":2, "data":[ { "id":1, "email":"george.bluth@reqres.in", "first_name":"George", "last_name":"Bluth", "avatar":"https://reqres.in/img/faces/1-image.jpg" }, { "id":2, "email":"janet.weaver@reqres.in", "first_name":"Janet", "last_name":"Weaver", "avatar":"https://reqres.in/img/faces/2-image.jpg" }, { "id":3, "email":"emma.wong@reqres.in", "first_name":"Emma", "last_name":"Wong", "avatar":"https://reqres.in/img/faces/3-image.jpg" }, { "id":4, "email":"eve.holt@reqres.in", "first_name":"Eve", "last_name":"Holt", "avatar":"https://reqres.in/img/faces/4-image.jpg" }, { "id":5, "email":"charles.morris@reqres.in", "first_name":"Charles", "last_name":"Morris", "avatar":"https://reqres.in/img/faces/5-image.jpg" }, { "id":6, "email":"tracey.ramos@reqres.in", "first_name":"Tracey", "last_name":"Ramos", "avatar":"https://reqres.in/img/faces/6-image.jpg" }], "support":{ "url":"https://reqres.in/#support-heading", "text":"To keep ReqRes free, contributions towards server costs are appreciated!" } } |
we are going to use Retrofit network library which is a type safe HTTP client, add requred dependencies to gradle file
api 'com.squareup.retrofit2:retrofit:2.9.0' api 'com.squareup.retrofit2:converter-gson:2.9.0' |
Add Internet Permission in AndroidManifest.xm
Add Glide dependency
To work with images we are using the glide image cache library
implementation 'com.github.bumptech.glide:glide:4.12.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' |
Create a data class for UserInfo which is used to parse the json data from server
import com.google.gson.annotations.SerializedName data class UserInfo( val id: String, val email: String, @SerializedName("first_name") val firstName: String, @SerializedName("last_name") val lastName: String, val avatar: String ) |
Create Response Object
data class GetUsersResponse( val page: Int, val data: List, val support: Support, @SerializedName("total_pages") val totalPages: Int, @SerializedName("per_page") val perPage: Int, ) data class Support( val url: String, val text: String ) |
To work with retrofit we need to create interface which will comunicate server by respected apis.
Create interface for api
package com.example.jetpack.datamodels import retrofit2.http.GET import retrofit2.http.Headers import retrofit2.http.Query interface Api { @Headers("Accept: Application/json") @GET("users") suspend fun getUserList(@Query("page") page: Int): GetUsersResponse } |
Create Rest Client
package com.example.jetpack.datamodels import com.google.gson.GsonBuilder import okhttp3.OkHttpClient import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import java.util.concurrent.TimeUnit class RestClient{ private val service: Api fun getService(): Api { return service } init { val baseUrl = "https://reqres.in/api/" val client = OkHttpClient.Builder() client.connectTimeout(2, TimeUnit.MINUTES) client.readTimeout(2, TimeUnit.MINUTES) client.writeTimeout(2, TimeUnit.MINUTES) val gson = GsonBuilder() .serializeNulls() .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") .setLenient() .create() val retrofit = Retrofit.Builder() .baseUrl(baseUrl) .addConverterFactory(GsonConverterFactory.create(gson)) .client(client.build()) .build() service = retrofit.create(Api::class.java) } } |
We need a data source that will handle pagination. Lets create that
package com.example.jetpack.datamodels import androidx.paging.PagingSource import androidx.paging.PagingState import retrofit2.HttpException import java.io.IOException class UserPagingSource : PagingSource() { override fun getRefreshKey(state: PagingState): Int? { return state.anchorPosition } override suspend fun load(params: LoadParams): LoadResult { return try { val page = params.key ?: 1 val userListResponse = RestClient().getService().getUserList(page) LoadResult.Page( data = userListResponse.data, prevKey = if (page == 1) null else page - 1, nextKey = if (userListResponse.data.isEmpty()) null else page + 1 ) } catch (exception: IOException) { return LoadResult.Error(exception) } catch (exception: HttpException) { return LoadResult.Error(exception) } } } |
Let's configure pager in view model
class UserInfoViewModel(appObj: Application) : AndroidViewModel(appObj) { val users: Flow> = Pager(PagingConfig(pageSize = 6)) { UserPagingSource() }.flow.cachedIn(viewModelScope) } |
lets create ui for userinfo
@Composable fun UserInfoView(userInfo: UserInfo?) { Card( elevation = 4.dp, content = { Row( modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp, vertical = 16.dp), content = { val bitmap = remember { mutableStateOf(null) } val context = LocalContext.current Glide.with(context) .asBitmap() .load(userInfo?.avatar ?: "") .into(object : CustomTarget() { override fun onLoadCleared(placeholder: Drawable?) {} override fun onResourceReady( resource: Bitmap, transition: Transition? ) { bitmap.value = resource } }) val value = bitmap.value if (value != null) Image( value.asImageBitmap(), modifier = Modifier .clip(RoundedCornerShape(10.dp)).width(80.dp).height(80.dp), contentScale = ContentScale.Crop, contentDescription = "image" ) else Box( content = { Text( text = "Loading", fontSize = 16.sp, ) }, modifier = Modifier .width(80.dp) .height(100.dp) .border( width = 1.2.dp, color = Color.White, shape = RectangleShape ), contentAlignment = Alignment.Center ) Spacer(modifier = Modifier.size(16.dp)) Column( modifier = Modifier.weight(2F), content = { Text( text = "${userInfo?.firstName} ${userInfo?.lastName}", fontSize = 20.sp, maxLines = 2, overflow = TextOverflow.Ellipsis, fontWeight = FontWeight.Bold ) Text( text = "${userInfo?.email}", color = Color.Gray, maxLines = 1, overflow = TextOverflow.Ellipsis ) }) }) }, modifier = Modifier .fillMaxWidth() .padding(top = 16.dp, start = 16.dp, end = 16.dp), ) } |
Complete example code for Jetpack compose Pagination
package com.example.jetpack import android.graphics.Bitmap import android.graphics.drawable.Drawable import android.os.Bundle import android.view.WindowManager import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.Image import androidx.compose.foundation.border import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.lifecycle.ViewModelProvider import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.items import com.bumptech.glide.Glide import com.bumptech.glide.request.target.CustomTarget import com.bumptech.glide.request.transition.Transition import com.example.jetpack.datamodels.UserInfo import com.example.jetpack.ui.theme.JetPackTheme import com.example.jetpack.viewmodels.UserInfoViewModel import com.google.accompanist.pager.* class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) val userInfoViewModel = ViewModelProvider(this).get(UserInfoViewModel::class.java) setContent { PaginationDemo(userInfoViewModel) } } } @Composable fun PaginationDemo(userInfoViewModel: UserInfoViewModel) { val users: LazyPagingItems = userInfoViewModel.users.collectAsLazyPagingItems() JetPackTheme(darkTheme = true) { Scaffold( content = { LazyColumn { items(users) { item -> UserInfoView(item) } } }, modifier = Modifier.fillMaxSize(), topBar = { TopAppBar( title = { Text("Users") } ) }, ) } } @Composable fun UserInfoView(userInfo: UserInfo?) { Card( elevation = 4.dp, content = { Row( modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp, vertical = 16.dp), content = { val bitmap = remember { mutableStateOf(null) } val context = LocalContext.current Glide.with(context) .asBitmap() .load(userInfo?.avatar ?: "") .into(object : CustomTarget() { override fun onLoadCleared(placeholder: Drawable?) {} override fun onResourceReady( resource: Bitmap, transition: Transition? ) { bitmap.value = resource } }) val value = bitmap.value if (value != null) Image( value.asImageBitmap(), modifier = Modifier .clip(RoundedCornerShape(10.dp)).width(80.dp).height(80.dp), contentScale = ContentScale.Crop, contentDescription = "image" ) else Box( content = { Text( text = "Loading", fontSize = 16.sp, ) }, modifier = Modifier .width(80.dp) .height(100.dp) .border( width = 1.2.dp, color = Color.White, shape = RectangleShape ), contentAlignment = Alignment.Center ) Spacer(modifier = Modifier.size(16.dp)) Column( modifier = Modifier.weight(2F), content = { Text( text = "${userInfo?.firstName} ${userInfo?.lastName}", fontSize = 20.sp, maxLines = 2, overflow = TextOverflow.Ellipsis, fontWeight = FontWeight.Bold ) Text( text = "${userInfo?.email}", color = Color.Gray, maxLines = 1, overflow = TextOverflow.Ellipsis ) }) }) }, modifier = Modifier .fillMaxWidth() .padding(top = 16.dp, start = 16.dp, end = 16.dp), ) } @ExperimentalPagerApi @Preview(showBackground = true) @Composable fun DefaultPreview() { } |
Conclusion: In this Jetpack compose tutorial we covered fetch data from rest api with pagination implemention using Pager compose function.
Article Contributed By :
|
|
|
|
2523 Views |