Android Jetpack Compose API Data to List View

Search for a command to run...

No comments yet. Be the first to comment.
Model–View–ViewModel (MVVM) is a software architectural pattern that facilitates the separation of the development of the GUI (the view) from the development of the business logic or back-end logic (the model) so that the view is not dependent on any...
EnvironmentObject is useful when you want to create a dependency in a higher component of the layout tree and use it on a lower component without having to pass it down the tree through every child component. We will now use EnvironmentObject to moni...

CompositionLocal is useful when you want to create a dependency in a higher node of the layout tree and use it on a lower node without having to pass it down the tree through every child Composable. Here we will use it to direct our application when ...

The TDD (Test Driven Development) approach is important when it comes to testing small portions of business logic code in isolation. In this blog, I'll cover how I would use TDD to build a clean architecture To-do SwiftUI application I'll cover imp...

The TDD (Test Driven Development) approach is important when it comes to testing small portions of business logic code in isolation. In this blog, I'll cover how I would use TDD to build a clean architecture To-do Jetpack Compose Android application ...

The Room persistence library provides an abstraction layer over SQLite. To use Room in your app, add the following dependencies to your app’s build.gradle file: dependencies { .... kapt "org.xerial:sqlite-jdbc:3.34.0" // Room def roo...

Many times we need to make API calls to fetch data and display that data using a List. Here, I show how to do that with Compose. To illustrate the structure of the application let’s look at the following diagram

Firstly, Add Internet Permission to your Application in AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
Before we start let’s update our build.gradle file with the Retrofit HTTP client and aConverter which uses Gson for serialisation to and from JSON.:
dependencies{
...
implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation "com.squareup.retrofit2:converter-gson:2.0.0"
}
We need to create the Retrofit instance to send the network requests. We need to use the Retrofit Builder class and specify the base URL for the service. Here we have one GET to fetch all Todos and deserialise to List
data class Todo(
var userId: Int,
var id: Int,
var title: String,
var completed: Boolean
)
const val BASE_URL = "https://jsonplaceholder.typicode.com/"
interface APIService {
@GET("todos")
suspend fun getTodos(): List<Todo>
companion object {
var apiService: APIService? = null
fun getInstance(): APIService {
if (apiService == null) {
apiService = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build().create(APIService::class.java)
}
return apiService!!
}
}
}
The View model publishes the todoList and has a getTodoList function to fetch all todos
class TodoViewModel : ViewModel() {
private val _todoList = mutableStateListOf<Todo>()
var errorMessage: String by mutableStateOf("")
val todoList: List<Todo>
get() = _todoList
fun getTodoList() {
viewModelScope.launch {
val apiService = APIService.getInstance()
try {
_todoList.clear()
_todoList.addAll(apiService.getTodos())
} catch (e: Exception) {
errorMessage = e.message.toString()
}
}
}
}
Finally we have the view which observes the ViewModel for any todo list state changes. When changes are detected the composable rebuilds. The todo list is displayed in the view.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
val vm = TodoViewModel()
super.onCreate(savedInstanceState)
setContent {
MaterialTheme {
TodoView(vm)
}
}
}
}
@Composable
fun TodoView(vm: TodoViewModel) {
LaunchedEffect(Unit, block = {
vm.getTodoList()
})
Scaffold(
topBar = {
TopBar()
},
content = {
if (vm.errorMessage.isEmpty()) {
TodoList(vm)
} else {
Text(vm.errorMessage)
}
}
)
}
@Composable
private fun TodoList(vm: TodoViewModel) {
Column(modifier = Modifier.padding(16.dp)) {
LazyColumn(modifier = Modifier.fillMaxHeight()) {
items(vm.todoList) { todo ->
Column {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(0.dp, 0.dp, 16.dp, 0.dp)
) {
Text(
todo.title,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
Spacer(modifier = Modifier.width(16.dp))
Checkbox(checked = todo.completed, onCheckedChange = null)
}
Divider()
}
}
}
}
}
@Composable
private fun TopBar() {
TopAppBar(
title = {
Row {
Text("Todos")
}
})
}
