How to Create Expandable Listview with Jetpack compose - Expanded Listview

Last updated Sep 28, 2021

In this Jetpack compose tutorial we will learn how to Create Expandable Listview with Jetpack compose in Android application. Expanded Listview is a list where we can show some data and hide some data .Basically its a two-level parent-child list where children are hide until the list item is expanded.

 

Let's get started

Step 1: Create android application in android studio

Step 2: Follow step for setup Jetpack Compose with Android Studio

 

In this example we are managing the data inside listview with Viewmodel and kotlin data classes

Lets create a Expanded List Item data class

data class User(
    val id: String = "",
    val name: String = "",
    val emailId: String = ""
)

data class ExpandedListItem(val data: User, val index: Int, var expanded: Boolean = false)

 

View Model

package com.example.jetpack.viewmodels

import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.example.jetpack.ExpandedListItem
import com.example.jetpack.datamodels.User
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch

class UserViewModel(appObj: Application) : AndroidViewModel(appObj) {
    private val _users = MutableStateFlow(listOf())
    val users: StateFlow> get() = _users

    fun changeItemValue(index: Int, expanded: Boolean) {
        val list = _users.value.toMutableList()
        list[index] = list[index].copy(expanded = expanded)
        _users.value = list.toList()
    }
    init {
        fetchUsers()
    }

    private fun fetchUsers() {
        viewModelScope.launch {
            val userList = arrayListOf(
                ExpandedListItem(
                    data = User(id = "1", name = "Ankit Singh", emailId = "an@gmail.com"),
                    index = 0
                ),
                ExpandedListItem(
                    data = User(id = "2", name = "Neha Shaw", emailId = "ne@gmail.com"),
                    index = 1
                ),
                ExpandedListItem(
                    data = User(id = "3", name = "Arpita Ghosh", emailId = "ar@gmail.com"),
                    index = 2
                ),
                ExpandedListItem(
                    data = User(id = "4", name = "Akash Tiwari", emailId = "ak@gmail.com"),
                    index = 3
                ),
                ExpandedListItem(
                    data = User(id = "5", name = "Anisha Tiwari", emailId = "an@gmail.com"),
                    index = 4
                ),
                ExpandedListItem(
                    data = User(id = "6", name = "Rowdy Rathore", emailId = "ro@gmail.com"),
                    index = 5
                ),
                ExpandedListItem(
                    data = User(id = "7", name = "Jit Singh", emailId = "ji@gmail.com"),
                    index = 6
                ),
                ExpandedListItem(
                    data = User(id = "8", name = "Pravin Raj", emailId = "pr@gmail.com"),
                    index = 7
                ),
                ExpandedListItem(
                    data = User(id = "9", name = "Sneha Rao", emailId = "sn@gmail.com"),
                    index = 8
                ),
                ExpandedListItem(
                    data = User(id = "10", name = "Ranjana Rathore", emailId = "ran@gmail.com"),
                    index = 9
                ),
                ExpandedListItem(
                    data = User(id = "11", name = "Kamala Rathore", emailId = "ka@gmail.com"),
                    index = 10
                ),
                ExpandedListItem(
                    data = User(id = "12", name = "Ranjani Rathore", emailId = "raj@gmail.com"),
                    index = 11
                ),
                ExpandedListItem(
                    data = User(id = "13", name = "XYZ Rathore", emailId = "xyz@gmail.com"),
                    index = 12
                ),
                ExpandedListItem(
                    data = User(id = "14", name = "ZAR Rathore", emailId = "zar@gmail.com"),
                    index = 13
                )
            )
            _users.emit(userList)
        }
    }
}

 

Here we have used stateFlow which emits list of user whenever user data will changed that can be observed while showing list items.

 

Lets create UI to display data inside listview with exapandable

@Composable
fun ExpandableListDemo(userViewModel: UserViewModel) {
    val users = userViewModel.users.collectAsState()

    JetPackTheme() {
        LazyColumn(contentPadding = PaddingValues(horizontal = 16.dp)) {
            itemsIndexed(items = users.value, itemContent = { pos, item ->
                Column(content = {
                    ExpandedListItemView(
                        item,
                        onButtonClicked = { id: String, expanded: Boolean, index: Int ->
                            userViewModel.changeItemValue(
                                index,
                                expanded = expanded
                            )
                        })
                    if (item.expanded) {
                        UserListItemView(user = item.data)
                    }
                })
            })

        }
    }
}

@Composable
fun ExpandedListItemView(
    item: ExpandedListItem,
    onButtonClicked: ((id: String, expanded: Boolean, index: Int) -> Unit)? = null
) {
    Row(
        content = {
            Text(item.data.name, modifier = Modifier.weight(1F), fontWeight = FontWeight.Bold)
            Icon(
                imageVector = if (item.expanded) Icons.Default.ArrowDropUp else Icons.Default.ArrowDropDown,
                contentDescription = "image",
                tint = Color.Black, modifier = Modifier
                    .size(30.dp)
                    .clickable(onClick = {
                        onButtonClicked?.invoke(item.data.id, !item.expanded, item.index)
                    })
            )
        }, modifier = Modifier
            .fillMaxWidth()
            .padding(top = 16.dp)
    )
}

@Composable
fun UserListItemView(user: User) {
    Column(content = {
        Text(text = user.emailId, color = Color.Gray)
    })
}

 

To display the header list item data we will use below compose function

@Composable
fun ExpandedListItemView(
    item: ExpandedListItem,
    onButtonClicked: ((id: String, expanded: Boolean, index: Int) -> Unit)? = null
) {
    Row(
        content = {
            Text(item.data.name, modifier = Modifier.weight(1F), fontWeight = FontWeight.Bold)
            Icon(
                imageVector = if (item.expanded) Icons.Default.ArrowDropUp else Icons.Default.ArrowDropDown,
                contentDescription = "image",
                tint = Color.Black, modifier = Modifier
                    .size(30.dp)
                    .clickable(onClick = {
                        onButtonClicked?.invoke(item.data.id, !item.expanded, item.index)
                    })
            )
        }, modifier = Modifier
            .fillMaxWidth()
            .padding(top = 16.dp)
    )
}

When we tap on the header item we are showing the child data with the below compose function

@Composable
fun UserListItemView(user: User) {
    Column(content = {
        Text(text = user.emailId, color = Color.Gray)
    })
}

 

Now let's run the application, you can see the data inside expandable listview, when we tap on header list item, it will expand and display the child list items.

 

Jetpack compose Expanded Listview

 

 

How to create Expandable listview with Jetpack compose

 

Full code

package com.example.jetpack

import android.os.Bundle
import android.view.WindowManager
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModelProvider
import com.example.jetpack.datamodels.User
import com.example.jetpack.ui.theme.JetPackTheme
import com.example.jetpack.viewmodels.UserViewModel

class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
        val userViewModel = ViewModelProvider(this).get(UserViewModel::class.java)
        setContent {
            ExpandableListDemo(userViewModel)
        }
    }
}


data class ExpandedListItem(val data: User, val index: Int, var expanded: Boolean = false)

@Composable
fun ExpandableListDemo(userViewModel: UserViewModel) {
    val users = userViewModel.users.collectAsState()

    JetPackTheme() {
        LazyColumn(contentPadding = PaddingValues(horizontal = 16.dp)) {
            itemsIndexed(items = users.value, itemContent = { pos, item ->
                Column(content = {
                    ExpandedListItemView(
                        item,
                        onButtonClicked = { id: String, expanded: Boolean, index: Int ->
                            userViewModel.changeItemValue(
                                index,
                                expanded = expanded
                            )
                        })
                    if (item.expanded) {
                        UserListItemView(user = item.data)
                    }
                })
            })

        }
    }
}

@Composable
fun ExpandedListItemView(
    item: ExpandedListItem,
    onButtonClicked: ((id: String, expanded: Boolean, index: Int) -> Unit)? = null
) {
    Row(
        content = {
            Text(item.data.name, modifier = Modifier.weight(1F), fontWeight = FontWeight.Bold)
            Icon(
                imageVector = if (item.expanded) Icons.Default.ArrowDropUp else Icons.Default.ArrowDropDown,
                contentDescription = "image",
                tint = Color.Black, modifier = Modifier
                    .size(30.dp)
                    .clickable(onClick = {
                        onButtonClicked?.invoke(item.data.id, !item.expanded, item.index)
                    })
            )
        }, modifier = Modifier
            .fillMaxWidth()
            .padding(top = 16.dp)
    )
}

@Composable
fun UserListItemView(user: User) {
    Column(content = {
        Text(text = user.emailId, color = Color.Gray)
    })
}

 

Conclusion: In this jetpack compose tutorial we covered how to create expandable listview with jetpack compose and handle the expand and collapse the list items.

Article Contributed By :
https://www.rrtutors.com/site_assets/profile/assets/img/avataaars.svg

2727 Views