Quick Start
This guide shows the most common usage pattern: a Scaffold with a MaterialContextualTopAppBar and a LazyColumn where list items can be selected.
Full Working Example
import androidx.compose.foundation.combinedClickableimport androidx.compose.foundation.layout.*import androidx.compose.foundation.lazy.LazyColumnimport androidx.compose.foundation.lazy.itemsimport androidx.compose.material.icons.Iconsimport androidx.compose.material.icons.filled.Archiveimport androidx.compose.material.icons.filled.Deleteimport androidx.compose.material.icons.filled.MoreVertimport androidx.compose.material3.*import androidx.compose.runtime.*import androidx.compose.ui.Alignmentimport androidx.compose.ui.Modifierimport androidx.compose.ui.unit.dpimport io.github.aldefy.contextualappbar.MaterialContextualTopAppBar
@OptIn(ExperimentalMaterial3Api::class)@Composablefun InboxScreen() { val emails = remember { (1..20).map { "Email #$it — Subject line goes here" } } var selectedIds by remember { mutableStateOf(emptySet<Int>()) }
Scaffold( topBar = { MaterialContextualTopAppBar( selectedCount = selectedIds.size, onClearSelection = { selectedIds = emptySet() }, defaultBar = { TopAppBar( title = { Text("Inbox") }, actions = { IconButton(onClick = { }) { Icon(Icons.Default.MoreVert, contentDescription = "More") } } ) }, contextualActions = { IconButton(onClick = { // handle delete for selectedIds selectedIds = emptySet() }) { Icon(Icons.Default.Delete, contentDescription = "Delete") } IconButton(onClick = { // handle archive for selectedIds selectedIds = emptySet() }) { Icon(Icons.Default.Archive, contentDescription = "Archive") } } ) } ) { padding -> LazyColumn(contentPadding = padding) { items(emails.indices.toList(), key = { it }) { index -> val isSelected = index in selectedIds
ListItem( headlineContent = { Text(emails[index]) }, supportingContent = { Text("Tap to open, long-press to select") }, leadingContent = { if (isSelected) { Checkbox(checked = true, onCheckedChange = null) } else { Checkbox(checked = false, onCheckedChange = null) } }, modifier = Modifier .combinedClickable( onClick = { if (selectedIds.isNotEmpty()) { // In selection mode, tap toggles selectedIds = if (index in selectedIds) { selectedIds - index } else { selectedIds + index } } // else: open email }, onLongClick = { selectedIds = selectedIds + index } ) ) HorizontalDivider() } } }}How Selection Works
- The user long-presses a list item — you add it to
selectedIds. selectedIds.sizeis passed toselectedCount. When it becomes> 0, the contextual bar animates in.- The contextual bar shows “N selected” by default and renders your
contextualActions. - When the user taps the close icon or presses the system back button,
onClearSelectionis called, emptyingselectedIds. selectedCountdrops to0and the default bar animates back in.
Next Steps
- ContextualTopAppBar guide — use the raw composable for a fully custom layout
- MaterialContextualTopAppBar guide — all parameters explained
- Customization guide — custom colors, animations, disabling back press