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.