FilterChip & InputChip in Compose: Tag & Filter UI Guide
Jetpack Compose Material3 provides powerful chip components for building modern tag and filter UIs. Let's explore FilterChip for filtering and InputChip for tag input.
FilterChip: Single & Multi-Select Filters
FilterChip is perfect for selecting multiple filter options. It supports both selected and unselected states.
@Composable
fun FilterChipExample() {
var selectedFilters by remember { mutableStateOf(setOf("Kotlin")) }
Row(modifier = Modifier.horizontalScroll(rememberScrollState())) {
listOf("Kotlin", "Android", "Compose", "Material3").forEach { filter ->
FilterChip(
selected = filter in selectedFilters,
onClick = {
selectedFilters = if (filter in selectedFilters) {
selectedFilters - filter
} else {
selectedFilters + filter
}
},
label = { Text(filter) },
modifier = Modifier.padding(4.dp)
)
}
}
}
Key Features:
- selected: Boolean state for selection
- onClick: Toggle selection
- label: Chip text
- leadingIcon: Optional icon (gear, checkmark)
- trailingIcon: Optional trailing content
InputChip: Deletable Tags
InputChip is for user-entered or selectable tags that can be deleted. Perfect for tag input fields.
@Composable
fun InputChipExample() {
var tags by remember { mutableStateOf(listOf("Compose", "Material3")) }
var newTag by remember { mutableStateOf("") }
Column {
FlowRow(modifier = Modifier.padding(8.dp)) {
tags.forEach { tag ->
InputChip(
selected = true,
onClick = {},
label = { Text(tag) },
trailingIcon = {
Icon(
Icons.Default.Close,
contentDescription = "Remove",
modifier = Modifier
.size(18.dp)
.clickable {
tags = tags - tag
}
)
},
modifier = Modifier.padding(4.dp)
)
}
}
TextField(
value = newTag,
onValueChange = { newTag = it },
modifier = Modifier.fillMaxWidth(),
placeholder = { Text("Add tag...") }
)
}
}
AssistChip: Action Chips
AssistChip represents an action or command:
AssistChip(
onClick = { /* Handle action */ },
label = { Text("Share") },
leadingIcon = {
Icon(Icons.Default.Share, contentDescription = null)
}
)
FlowRow: Auto-Wrapping Layout
Use FlowRow to automatically wrap chips to the next line:
FlowRow(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
items.forEach { item ->
FilterChip(
selected = item.isSelected,
onClick = { /* Toggle */ },
label = { Text(item.name) }
)
}
}
LazyRow: Horizontal Scroll
For large chip lists, use LazyRow:
LazyRow(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(4.dp),
contentPadding = PaddingValues(8.dp)
) {
items(filters.size) { index ->
FilterChip(
selected = filters[index].isSelected,
onClick = { /* Toggle */ },
label = { Text(filters[index].name) }
)
}
}
Integration: List Filtering with Filters
Combine chips with list state:
@Composable
fun FilteredListScreen() {
var items by remember { mutableStateOf(listOf(...)) }
var selectedFilters by remember { mutableStateOf(setOf<String>()) }
val filtered = remember(items, selectedFilters) {
if (selectedFilters.isEmpty()) items
else items.filter { it.category in selectedFilters }
}
Column {
// Filter chips
FlowRow {
categories.forEach { cat ->
FilterChip(
selected = cat in selectedFilters,
onClick = {
selectedFilters = if (cat in selectedFilters) {
selectedFilters - cat
} else {
selectedFilters + cat
}
},
label = { Text(cat) }
)
}
}
// Filtered list
LazyColumn {
items(filtered.size) { index ->
ItemCard(filtered[index])
}
}
}
}
Best Practices
- Use FilterChip for filters - Clear visual feedback for active filters
- Use InputChip for tags - Supports delete action natively
- Combine with FlowRow - Natural wrapping without horizontal scroll
- Provide visual feedback - Use colors/icons to show state
- Test touch targets - Minimum 48dp recommended
- Remember state - Use remember() for selection state persistence
Conclusion
Chips are fundamental to Material Design 3. Master FilterChip and InputChip to build professional tag and filter interfaces in Jetpack Compose.
8 Android App Templates → https://myougatheax.gumroad.com
Top comments (0)