DEV Community

myougaTheAxo
myougaTheAxo

Posted on

FilterChip & InputChip in Compose: Tag & Filter UI Guide

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)
            )
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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...") }
        )
    }
}
Enter fullscreen mode Exit fullscreen mode

AssistChip: Action Chips

AssistChip represents an action or command:

AssistChip(
    onClick = { /* Handle action */ },
    label = { Text("Share") },
    leadingIcon = {
        Icon(Icons.Default.Share, contentDescription = null)
    }
)
Enter fullscreen mode Exit fullscreen mode

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) }
        )
    }
}
Enter fullscreen mode Exit fullscreen mode

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) }
        )
    }
}
Enter fullscreen mode Exit fullscreen mode

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])
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Best Practices

  1. Use FilterChip for filters - Clear visual feedback for active filters
  2. Use InputChip for tags - Supports delete action natively
  3. Combine with FlowRow - Natural wrapping without horizontal scroll
  4. Provide visual feedback - Use colors/icons to show state
  5. Test touch targets - Minimum 48dp recommended
  6. 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)