FilterChip in Jetpack Compose (with Examples)

Jetpack Compose Material 3 FilterChip

In this article, we’ll learn how to implement the Material 3 FilterChip in Jetpack Compose.

Prerequisites:

What is Filter Chip in Android?

Filter chips use tags or descriptive words to filter content. They can be a good alternative to toggle buttons or checkboxes.

Example:

FilterChip Colors

Let’s see how to implement them in the Android Studio.

First, create an empty Compose project and open the MainActivity.kt. Create a MyUI() composable and call it from the onCreate() method. We’ll write our code in it.

// add the following packages
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Done
import androidx.compose.material3.ElevatedFilterChip
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FilterChip
import androidx.compose.material3.FilterChipDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            YourProjectNameTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    Column(
                        modifier = Modifier
                            .fillMaxSize()
                            .padding(all = 6.dp)
                    ) {
                        MyUI()
                    }
                }
            }
        }
    }
}

@Composable
fun MyUI() {

}

Jetpack Compose provides FilterChip() method:

@ExperimentalMaterial3Api
@Composable
fun FilterChip(
    selected: Boolean,
    onClick: () -> Unit,
    label: @Composable () -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    leadingIcon: @Composable (() -> Unit)? = null,
    trailingIcon: @Composable (() -> Unit)? = null,
    shape: Shape = FilterChipDefaults.shape,
    colors: SelectableChipColors = FilterChipDefaults.filterChipColors(),
    elevation: SelectableChipElevation? = FilterChipDefaults.filterChipElevation(),
    border: SelectableChipBorder? = FilterChipDefaults.filterChipBorder(),
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
)

selected – Whether this chip is selected or not.

onClick – It is a lambda that gets called when we tap on this chip.

label – Text on this chip. We use Text() composable to add the label.

modifier – The Modifier to be applied to this chip.

enabled – If the chip is enabled or not. When false, this component will not respond to user input, and it will appear visually disabled and disabled to accessibility services.

leadingIcon – An optional icon at the start of the chip. When selected is true, this icon may visually indicate that the chip is selected (for example, via a checkmark icon).

trailingIcon – An optional icon at the end of the chip.

shapeShape of this chip.

colors – Text, icon, and background colors of this chip in different states.

elevation – The shadow below the chip.

border – The border to draw around the container of this chip. Pass null for no border.

interactionSource – It is used to observe and customize the interactions. For example, we can disable the ripple effect.

Note: As of today, FilterChip() is experimental.

Simple FilterChip Example:

The selected, onClick, and label parameters are mandatory.

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MyUI() {
    var selected by remember {
        mutableStateOf(false) // initially not selected
    }

    val contextForToast = LocalContext.current.applicationContext

    FilterChip(
        selected = selected,
        onClick = {
            selected = !selected
            Toast.makeText(contextForToast, "$selected", Toast.LENGTH_SHORT).show()
        },
        label = {
            Text(text = "Filter Chip")
        }
    )
}

Output:

Simple FilterChip Example

List of FilterChips:

To display multiple chips, put them in a LazyRow.

Single-selection:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MyUI() {
    val itemsList = listOf("Android", "iOS", "Windows", "MAC", "Linux")

    val contextForToast = LocalContext.current.applicationContext

    var selectedItem by remember {
        mutableStateOf(itemsList[0]) // initially, first item is selected
    }

    LazyRow(modifier = Modifier.fillMaxWidth()) {
        items(itemsList) { item ->
            FilterChip(
                modifier = Modifier.padding(horizontal = 6.dp), // gap between items
                selected = (item == selectedItem),
                onClick = {
                    selectedItem = item
                    Toast.makeText(contextForToast, selectedItem, Toast.LENGTH_SHORT).show()
                },
                label = {
                    Text(text = item)
                }
            )
        }
    }
}

Output:

List of FilterChips Single Selection

Multi-selection:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MyUI() {
    val itemsList = listOf("Android", "iOS", "Windows", "MAC", "Linux")

    val contextForToast = LocalContext.current.applicationContext

    val selectedItems = remember {
        mutableStateListOf(itemsList[0]) // initially, first item is selected
    }

    LazyRow(modifier = Modifier.fillMaxWidth()) {
        items(itemsList) { item ->
            FilterChip(
                modifier = Modifier.padding(horizontal = 6.dp), // gap between items
                selected = selectedItems.contains(item),
                onClick = {
                    if (selectedItems.contains(item)) {
                        selectedItems.remove(item)
                    } else {
                        selectedItems.add(item)
                    }
                    Toast.makeText(contextForToast, selectedItems.joinToString(), Toast.LENGTH_SHORT)
                        .show()
                },
                label = {
                    Text(text = item)
                }
            )
        }
    }
}

Output:

List of FilterChips Multi Selection

Related: Lazy Grid Layouts in Jetpack Compose (with Examples)

FilterChip with Icon:

We can easily add the icon using the leadingIcon parameter and Icon() composable.

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MyUI() {
    val itemsList = listOf("Android", "iOS", "Windows", "MAC", "Linux")

    val contextForToast = LocalContext.current.applicationContext

    var selectedItem by remember {
        mutableStateOf(itemsList[0]) // initially, first item is selected
    }

    LazyRow(modifier = Modifier.fillMaxWidth()) {
        items(itemsList) { item ->
            FilterChip(
                modifier = Modifier.padding(horizontal = 6.dp), // gap between items
                selected = (item == selectedItem),
                onClick = {
                    selectedItem = item
                    Toast.makeText(contextForToast, selectedItem, Toast.LENGTH_SHORT).show()
                },
                label = {
                    Text(text = item)
                },
                leadingIcon = if (item == selectedItem) {
                    {
                        Icon(
                            imageVector = Icons.Default.Done,
                            contentDescription = null,
                            modifier = Modifier.size(
                                FilterChipDefaults.IconSize
                            )
                        )
                    }
                } else {
                    null
                }
            )
        }
    }
}

Output:

FilterChip with Icon

FilterChip Colors:

There are two methods to change the colors: filterChipColors() and filterChipBorder().

Using the filterChipColors(), we can change the background, text, and icon colors.

@Composable
public final fun filterChipColors(
    containerColor: Color,
    labelColor: Color,
    iconColor: Color,
    disabledContainerColor: Color,
    disabledLabelColor: Color,
    disabledLeadingIconColor: Color,
    disabledTrailingIconColor: Color,
    selectedContainerColor: Color,
    disabledSelectedContainerColor: Color,
    selectedLabelColor: Color,
    selectedLeadingIconColor: Color,
    selectedTrailingIconColor: Color
): SelectableChipColors

In Material 3, containerColor represents the background color.

Using the filterChipBorder(), we can customize the border.

@Composable
public final fun filterChipBorder(
    borderColor: Color,
    selectedBorderColor: Color,
    disabledBorderColor: Color,
    disabledSelectedBorderColor: Color,
    borderWidth: Dp,
    selectedBorderWidth: Dp
): SelectableChipBorder

Example:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MyUI(highlightColor: Color = Color(0xFFE91E63)) {
    val itemsList = listOf("Android", "iOS", "Windows", "MAC", "Linux")

    val contextForToast = LocalContext.current.applicationContext

    var selectedItem by remember {
        mutableStateOf(itemsList[0]) // initially, first item is selected
    }

    LazyRow(modifier = Modifier.fillMaxWidth()) {
        items(itemsList) { item ->
            FilterChip(
                modifier = Modifier.padding(all = 6.dp), // gap between items
                selected = (item == selectedItem),
                onClick = {
                    selectedItem = item
                    Toast.makeText(contextForToast, selectedItem, Toast.LENGTH_SHORT).show()
                },
                label = {
                    Text(
                        text = item,
                        fontWeight = if (item == selectedItem) FontWeight.Medium else FontWeight.Normal
                    )
                },
                leadingIcon = if (item == selectedItem) {
                    {
                        Icon(
                            imageVector = Icons.Default.Done,
                            contentDescription = null,
                            modifier = Modifier.size(
                                FilterChipDefaults.IconSize
                            )
                        )
                    }
                } else {
                    null
                },
                colors = FilterChipDefaults.filterChipColors(
                    labelColor = highlightColor,
                    selectedLabelColor = highlightColor,
                    selectedLeadingIconColor = highlightColor,
                    selectedContainerColor = highlightColor.copy(alpha = 0.1f)
                ),
                border = FilterChipDefaults.filterChipBorder(
                    selectedBorderColor = highlightColor,
                    selectedBorderWidth = 2.dp,
                    borderColor = highlightColor
                )
            )
        }
    }
}

Output:

FilterChip Colors

Related: Gradient Progressbar using Jetpack Compose

ElevatedFilterChip:

There is another version of the FilterChip() called ElevatedFilterChip(). It comes with the Material 3 elevated style and takes the same parameters as FilterChip().

@ExperimentalMaterial3Api
@Composable
fun ElevatedFilterChip(
    selected: Boolean,
    onClick: () -> Unit,
    label: @Composable () -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    leadingIcon: @Composable (() -> Unit)? = null,
    trailingIcon: @Composable (() -> Unit)? = null,
    shape: Shape = FilterChipDefaults.shape,
    colors: SelectableChipColors = FilterChipDefaults.elevatedFilterChipColors(),
    elevation: SelectableChipElevation? = FilterChipDefaults.elevatedFilterChipElevation(),
    border: SelectableChipBorder? = null,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
)

Example:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MyUI(highlightColor: Color = Color(0xFF009688)) {
    val itemsList = listOf("Android", "iOS", "Windows", "MAC", "Linux")

    val contextForToast = LocalContext.current.applicationContext

    var selectedItem by remember {
        mutableStateOf(itemsList[0]) // initially, first item is selected
    }

    LazyRow(modifier = Modifier.fillMaxWidth()) {
        items(itemsList) { item ->
            ElevatedFilterChip(
                modifier = Modifier.padding(all = 6.dp), // gap between items
                selected = (item == selectedItem),
                onClick = {
                    selectedItem = item
                    Toast.makeText(contextForToast, selectedItem, Toast.LENGTH_SHORT).show()
                },
                label = {
                    Text(
                        text = item,
                        fontWeight = if (item == selectedItem) FontWeight.Medium else FontWeight.Normal
                    )
                },
                leadingIcon = if (item == selectedItem) {
                    {
                        Icon(
                            imageVector = Icons.Default.Done,
                            contentDescription = null,
                            modifier = Modifier.size(
                                FilterChipDefaults.IconSize
                            )
                        )
                    }
                } else {
                    null
                },
                colors = FilterChipDefaults.elevatedFilterChipColors(
                    labelColor = highlightColor,
                    selectedLabelColor = highlightColor,
                    selectedLeadingIconColor = highlightColor
                    //selectedContainerColor = highlightColor.copy(alpha = 0.1f)
                ),
                elevation = FilterChipDefaults.elevatedFilterChipElevation(

                ),
                border = null
            )
        }
    }
}

Output:

ElevatedFilterChip Jetpack Compose

This is all about Material 3 FilterChip in Jetpack Compose. I hope you have learned something new. If you have any doubts, leave a comment below.

For more information, look at the Material and Android docs.

Related Articles:

Leave a Comment