Load data from api to recylerview using JetPack Compose
Hi today we look in to an concept of how to load data from an api using retrofit and load in to an recylerview using Jetpack Compose and single selection highlight on recylerview selection.
First we import things needed for retrofit in to our app level build gradle
//Retrofit implementation
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation "com.squareup.okhttp3:okhttp:5.0.0-alpha.2"
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
We are going to create a model data class of name Movie.kt with the following variables like name,category etc
data class Movie(val name:String,val category:String,val imageUrl:String,val desc:String)
Now lets create an interface class ApiService.kt to get and return data to viewmodel
interface ApiService {
@GET("movielist.json")
suspend fun getMovies():List<Movie>
companion object {
var apiService: ApiService? = null
fun getInstance() : ApiService {
if (apiService == null) {
apiService = Retrofit.Builder()
.baseUrl("https://howtodoandroid.com/apis/")
.addConverterFactory(GsonConverterFactory.create())
.build().create(ApiService::class.java)
}
return apiService!!
}
}
}
Next we are going to create ApiViewModel.kt we are launching our api call inside viewModelScope.launch so it will run as long as our viewmodel active on any failure we will be passing to our error message part.
class ApiViewModel@Inject constructor():ViewModel() {
var movieListResponse:List<Movie> by mutableStateOf(listOf())
var errorMessage: String by mutableStateOf("")
fun getMovieList() {
viewModelScope.launch {
val apiService = ApiService.getInstance()
try {
val movieList = apiService.getMovies()
movieListResponse = movieList
} catch (e: Exception) {
errorMessage = e.message.toString()
}
}
}
}
Now comes the ui part you can see we created a item for display MovieItem function
MovieList(movieList = viewModel.movieListResponse)
viewModel.getMovieList()@Composable
fun MovieList(movieList: List<Movie>) {
var selectedIndex by remember { mutableStateOf(-1) }
LazyColumn {
itemsIndexed(items = movieList) { index, item ->
MovieItem(movie = item, index, selectedIndex) { i ->
selectedIndex = i
}
}
}
}@Composable
fun MovieItem(movie: Movie, index: Int, selectedIndex: Int, onClick: (Int) -> Unit) {
val backgroundColor =
if (index == selectedIndex) MaterialTheme.colors.primary else MaterialTheme.colors.background
val textColour =
if (index == selectedIndex) Color.White else Color.Black
Card(
modifier = Modifier
.padding(8.dp, 4.dp)
.fillMaxWidth()
.clickable { onClick(index) }
.height(160.dp)
, shape = RoundedCornerShape(8.dp), elevation = 4.dp
) {
Surface(color = backgroundColor) {
Row(
Modifier
.padding(4.dp)
.fillMaxSize()
) {
Image(
painter = rememberImagePainter(
data = movie.imageUrl,
builder = {
scale(Scale.FILL)
placeholder(R.drawable.dummypuppy)
transformations(CircleCropTransformation())
}
),
contentDescription = movie.desc,
modifier = Modifier
.fillMaxHeight()
.weight(0.2f)
.padding(start = 10.dp)
)
Column(
verticalArrangement = Arrangement.Center,
modifier = Modifier
.padding(start=15.dp,end = 4.dp)
.fillMaxHeight()
.weight(0.8f)
) {
Text(
text = movie.name,
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
color=textColour,
fontFamily = noromalrobofonts
)
Text(
text = movie.category,
fontSize = 13.sp,color=Color.Black,fontFamily = noromalrobofonts,
modifier = Modifier
.background(
Color.Green
)
.padding(4.dp)
)
Text(
text = movie.desc,
fontSize = 14.sp,
color=textColour
,fontFamily = noromalrobofonts,
maxLines = 4,
overflow = TextOverflow.Ellipsis
)
}
}
}
}
}
Thats it we have our APi loaded to the recylerview.