Material 3 Components

Jetpack Compose Material 3 component reference — Cards, Dialogs, Snackbars, Chips, Tabs, Navigation, App Bars, Sheets, Menus, and more

Material 3 provides a rich set of pre-built components. This reference covers every major M3 component with usage examples.

Cards

Cards contain content and actions on a single topic.

// Elevated card (shadow, lifts on hover)
ElevatedCard(
    onClick = { /* navigate */ },
    modifier = Modifier.fillMaxWidth()
) {
    Text("Elevated card content")
}

// Filled card (solid background, no shadow)
FilledCard(
    modifier = Modifier.fillMaxWidth()
) {
    Text("Filled card content")
}

// Outlined card (border, no shadow)
OutlinedCard(
    modifier = Modifier.fillMaxWidth()
) {
    Text("Outlined card content")
}

// Card with rich content
ElevatedCard(
    modifier = Modifier.fillMaxWidth()
) {
    Column(modifier = Modifier.padding(16.dp)) {
        Text(
            text = "Card Title",
            style = MaterialTheme.typography.titleMedium
        )
        Spacer(Modifier.height(8.dp))
        Text(
            text = "Supporting text goes here.",
            style = MaterialTheme.typography.bodyMedium
        )
        Spacer(Modifier.height(12.dp))
        Row(horizontalArrangement = Arrangement.End, modifier = Modifier.fillMaxWidth()) {
            TextButton(onClick = { /* dismiss */ }) { Text("Dismiss") }
            TextButton(onClick = { /* learn more */ }) { Text("Learn More") }
        }
    }
}

Card elevation and interaction

// Clickable card with custom elevation
ElevatedCard(
    onClick = { /* handle */ },
    elevation = CardDefaults.elevatedCardElevation(
        defaultElevation = 2.dp,
        pressedElevation = 8.dp,
        hoveredElevation = 4.dp,
    )
) {
    Text("Interactive card")
}

Dialogs

AlertDialog

var showDialog by remember { mutableStateOf(false) }

if (showDialog) {
    AlertDialog(
        onDismissRequest = { showDialog = false },
        title = { Text("Confirm delete") },
        text = { Text("Are you sure you want to delete this item? This cannot be undone.") },
        confirmButton = {
            TextButton(onClick = {
                // Perform delete
                showDialog = false
            }) { Text("Delete") }
        },
        dismissButton = {
            TextButton(onClick = { showDialog = false }) { Text("Cancel") }
        },
        icon = { Icon(Icons.Default.Delete, contentDescription = null) },
    )
}

// Trigger
Button(onClick = { showDialog = true }) { Text("Delete item") }

DatePicker

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DatePickerExample() {
    var showPicker by remember { mutableStateOf(false) }
    var selectedDate by remember { mutableStateOf<LocalDate?>(null) }

    if (showPicker) {
        val state = rememberDatePickerState()

        DatePickerDialog(
            onDismissRequest = { showPicker = false },
            confirmButton = {
                TextButton(onClick = {
                    selectedDate = state.selectedDateMillis?.let {
                        Instant.fromEpochMilliseconds(it)
                            .atZone(ZoneId.systemDefault())
                            .toLocalDate()
                    }
                    showPicker = false
                }) { Text("OK") }
            },
            dismissButton = {
                TextButton(onClick = { showPicker = false }) { Text("Cancel") }
            }
        ) {
            DatePicker(state = state)
        }
    }

    OutlinedTextField(
        value = selectedDate?.toString() ?: "",
        onValueChange = {},
        readOnly = true,
        label = { Text("Select date") },
        trailingIcon = {
            IconButton(onClick = { showPicker = true }) {
                Icon(Icons.Default.CalendarMonth, "Pick date")
            }
        },
        modifier = Modifier.fillMaxWidth()
    )
}

TimePicker

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TimePickerExample() {
    var showPicker by remember { mutableStateOf(false) }
    val state = rememberTimePickerState()

    if (showPicker) {
        AlertDialog(
            onDismissRequest = { showPicker = false },
            confirmButton = {
                TextButton(onClick = { showPicker = false }) { Text("OK") }
            },
        ) {
            TimePicker(state = state)
        }
    }

    OutlinedTextField(
        value = "${state.hour}:${state.minute.toString().padStart(2, '0')}",
        onValueChange = {},
        readOnly = true,
        label = { Text("Select time") },
        trailingIcon = {
            IconButton(onClick = { showPicker = true }) {
                Icon(Icons.Default.Schedule, "Pick time")
            }
        },
    )
}

Snackbars

@Composable
fun SnackbarExample() {
    val snackbarHostState = remember { SnackbarHostState() }
    val scope = rememberCoroutineScope()

    Scaffold(
        snackbarHost = { SnackbarHost(snackbarHostState) }
    ) { innerPadding ->
        Column(
            modifier = Modifier.padding(innerPadding).padding(16.dp),
            verticalArrangement = Arrangement.spacedBy(8.dp)
        ) {
            // Simple snackbar
            Button(onClick = {
                scope.launch {
                    snackbarHostState.showSnackbar("Item deleted")
                }
            }) { Text("Show Snackbar") }

            // Snackbar with action
            Button(onClick = {
                scope.launch {
                    val result = snackbarHostState.showSnackbar(
                        message = "Item archived",
                        actionLabel = "Undo",
                        duration = SnackbarDuration.Short,
                    )
                    if (result == SnackbarResult.ActionPerformed) {
                        // Undo the action
                    }
                }
            }) { Text("Archive with undo") }
        }
    }
}

Chips

// Filter chip (selectable)
var selected by remember { mutableStateOf(false) }
FilterChip(
    selected = selected,
    onSelectedChange = { selected = it },
    label = { Text("Filter") },
    leadingIcon = if (selected) {
        { Icon(Icons.Default.Check, contentDescription = null, modifier = Modifier.size(16.dp)) }
    } else null,
)

// Input chip (removable, like a tag)
InputChip(
    selected = true,
    onClick = { /* toggle */ },
    label = { Text("Kotlin") },
    trailingIcon = {
        Icon(Icons.Default.Close, contentDescription = "Remove", modifier = Modifier.size(16.dp))
    },
)

// Suggestion chip (for search suggestions)
SuggestionChip(
    onClick = { /* apply suggestion */ },
    label = { Text("Elixir") },
    icon = { Icon(Icons.Default.Search, contentDescription = null, modifier = Modifier.size(16.dp)) },
)

// Assist chip (action shortcut)
AssistChip(
    onClick = { /* perform action */ },
    label = { Text("Add event") },
    leadingIcon = { Icon(Icons.Default.Add, contentDescription = null, modifier = Modifier.size(16.dp)) },
)

Tabs

@Composable
fun TabExample() {
    var selectedTab by remember { mutableStateOf(0) }
    val tabs = listOf("Home", "Search", "Profile")

    Column {
        // Fixed tabs (all tabs visible, equal width)
        TabRow(selectedTabIndex = selectedTab) {
            tabs.forEachIndexed { index, title ->
                Tab(
                    selected = selectedTab == index,
                    onClick = { selectedTab = index },
                    text = { Text(title) },
                    icon = {
                        when (index) {
                            0 -> Icon(Icons.Default.Home, contentDescription = null)
                            1 -> Icon(Icons.Default.Search, contentDescription = null)
                            2 -> Icon(Icons.Default.Person, contentDescription = null)
                        }
                    }
                )
            }
        }

        // Tab content
        when (selectedTab) {
            0 -> HomeTab()
            1 -> SearchTab()
            2 -> ProfileTab()
        }
    }
}

// Scrollable tabs (when you have many tabs)
@Composable
fun ScrollableTabExample() {
    var selectedTab by remember { mutableStateOf(0) }
    val tabs = (0..20).map { "Tab $it" }

    ScrollableTabRow(selectedTabIndex = selectedTab) {
        tabs.forEachIndexed { index, title ->
            Tab(
                selected = selectedTab == index,
                onClick = { selectedTab = index },
                text = { Text(title) }
            )
        }
    }
}

// Secondary tabs (for sub-sections within a page)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SecondaryTabExample() {
    var selected by remember { mutableStateOf(0) }
    val tabs = listOf("Day", "Week", "Month")

    SecondaryTabRow(selectedTabIndex = selected) {
        tabs.forEachIndexed { index, title ->
            Tab(
                selected = selected == index,
                onClick = { selected = index },
                text = { Text(title) }
            )
        }
    }
}

Top App Bars

// Small top app bar (default)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SmallTopBar() {
    TopAppBar(
        title = { Text("My App") },
        navigationIcon = {
            IconButton(onClick = { /* back */ }) {
                Icon(Icons.AutoMirrored.Filled.ArrowBack, "Back")
            }
        },
        actions = {
            IconButton(onClick = { /* search */ }) {
                Icon(Icons.Default.Search, "Search")
            }
            IconButton(onClick = { /* more */ }) {
                Icon(Icons.Default.MoreVert, "More")
            }
        },
    )
}

// Center-aligned top app bar
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CenterAlignedTopBar() {
    CenterAlignedTopAppBar(
        title = { Text("My App") },
        navigationIcon = {
            IconButton(onClick = { /* menu */ }) {
                Icon(Icons.Default.Menu, "Menu")
            }
        },
        actions = {
            IconButton(onClick = { /* search */ }) {
                Icon(Icons.Default.Search, "Search")
            }
        },
    )
}

// Medium top app bar (title moves up on scroll)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MediumTopBar() {
    val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()

    MediumTopAppBar(
        title = { Text("Settings") },
        scrollBehavior = scrollBehavior,
    )
}

// Large top app bar (collapses on scroll)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LargeTopBar() {
    val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()

    LargeTopAppBar(
        title = { Text("My Journal") },
        scrollBehavior = scrollBehavior,
    )
}

Bottom Navigation

@Composable
fun BottomNavExample() {
    var selectedItem by remember { mutableStateOf(0) }
    val items = listOf("Home", "Search", "Favorites", "Profile")

    Scaffold(
        bottomBar = {
            NavigationBar {
                NavigationBarItem(
                    selected = selectedItem == 0,
                    onClick = { selectedItem = 0 },
                    icon = { Icon(Icons.Default.Home, contentDescription = null) },
                    label = { Text(items[0]) },
                )
                NavigationBarItem(
                    selected = selectedItem == 1,
                    onClick = { selectedItem = 1 },
                    icon = { Icon(Icons.Default.Search, contentDescription = null) },
                    label = { Text(items[1]) },
                )
                NavigationBarItem(
                    selected = selectedItem == 2,
                    onClick = { selectedItem = 2 },
                    icon = { Icon(Icons.Default.Favorite, contentDescription = null) },
                    label = { Text(items[2]) },
                )
                NavigationBarItem(
                    selected = selectedItem == 3,
                    onClick = { selectedItem = 3 },
                    icon = { Icon(Icons.Default.Person, contentDescription = null) },
                    label = { Text(items[3]) },
                )
            }
        }
    ) { innerPadding ->
        ContentScreen(modifier = Modifier.padding(innerPadding), selectedTab = selectedItem)
    }
}
@Composable
fun NavigationRailExample() {
    var selectedItem by remember { mutableStateOf(0) }

    Row {
        NavigationRail {
            NavigationRailItem(
                selected = selectedItem == 0,
                onClick = { selectedItem = 0 },
                icon = { Icon(Icons.Default.Home, contentDescription = null) },
                label = { Text("Home") },
            )
            NavigationRailItem(
                selected = selectedItem == 1,
                onClick = { selectedItem = 1 },
                icon = { Icon(Icons.Default.Search, contentDescription = null) },
                label = { Text("Search") },
            )
            NavigationRailItem(
                selected = selectedItem == 2,
                onClick = { selectedItem = 2 },
                icon = { Icon(Icons.Default.Favorite, contentDescription = null) },
                label = { Text("Favorites") },
            )
        }
        ContentScreen(selectedTab = selectedItem)
    }
}
// Modal drawer (opens on top of content)
@Composable
fun ModalDrawerExample() {
    val drawerState = rememberDrawerState(DrawerValue.Closed)
    val scope = rememberCoroutineScope()

    ModalNavigationDrawer(
        drawerContent = {
            ModalDrawerSheet {
                Spacer(Modifier.height(12.dp))
                Text("Menu", modifier = Modifier.padding(16.dp), style = MaterialTheme.typography.titleMedium)
                HorizontalDivider()
                NavigationDrawerItem(
                    label = { Text("Home") },
                    selected = true,
                    onClick = { scope.launch { drawerState.close() } },
                    icon = { Icon(Icons.Default.Home, contentDescription = null) },
                )
                NavigationDrawerItem(
                    label = { Text("Settings") },
                    selected = false,
                    onClick = { scope.launch { drawerState.close() } },
                    icon = { Icon(Icons.Default.Settings, contentDescription = null) },
                )
            }
        },
        drawerState = drawerState
    ) {
        Scaffold(
            topBar = {
                TopAppBar(
                    title = { Text("App") },
                    navigationIcon = {
                        IconButton(onClick = { scope.launch { drawerState.open() } }) {
                            Icon(Icons.Default.Menu, "Menu")
                        }
                    }
                )
            }
        ) { innerPadding ->
            Content(modifier = Modifier.padding(innerPadding))
        }
    }
}

// Permanent drawer (always visible on large screens)
@Composable
fun PermanentDrawerExample() {
    PermanentNavigationDrawer(
        drawerContent = {
            PermanentDrawerSheet {
                Text("App", modifier = Modifier.padding(16.dp), style = MaterialTheme.typography.titleLarge)
                NavigationDrawerItem(
                    label = { Text("Inbox") },
                    selected = true,
                    onClick = { /* ... */ },
                    icon = { Icon(Icons.Default.Inbox, contentDescription = null) },
                    badge = { Text("12") },
                )
                NavigationDrawerItem(
                    label = { Text("Sent") },
                    selected = false,
                    onClick = { /* ... */ },
                    icon = { Icon(Icons.Default.Send, contentDescription = null) },
                )
            }
        }
    ) {
        Content()
    }
}

Floating Action Buttons

// Standard FAB
FloatingActionButton(
    onClick = { /* add */ },
) {
    Icon(Icons.Default.Add, contentDescription = "Add")
}

// Small FAB
SmallFloatingActionButton(
    onClick = { /* add */ },
) {
    Icon(Icons.Default.Add, contentDescription = "Add")
}

// Large FAB
LargeFloatingActionButton(
    onClick = { /* add */ },
) {
    Icon(Icons.Default.Add, contentDescription = "Add")
}

// Extended FAB (with text)
ExtendedFloatingActionButton(
    onClick = { /* compose */ },
    icon = { Icon(Icons.Default.Edit, contentDescription = null) },
    text = { Text("Compose") },
)

// Extended FAB that collapses to icon
var expanded by remember { mutableStateOf(true) }
ExtendedFloatingActionButton(
    onClick = { /* ... */ },
    expanded = expanded,
    icon = { Icon(Icons.Default.Edit, contentDescription = null) },
    text = { Text("Compose") },
)

Bottom Sheets

// Modal bottom sheet
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ModalBottomSheetExample() {
    var showSheet by remember { mutableStateOf(false) }

    if (showSheet) {
        ModalBottomSheet(
            onDismissRequest = { showSheet = false },
        ) {
            Column(modifier = Modifier.padding(16.dp)) {
                Text("Sheet Title", style = MaterialTheme.typography.titleLarge)
                Spacer(Modifier.height(8.dp))
                Text("Sheet content goes here.")
                Spacer(Modifier.height(24.dp))
            }
        }
    }

    Button(onClick = { showSheet = true }) { Text("Show sheet") }
}

// Standard bottom sheet (part of Scaffold)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun StandardBottomSheetExample() {
    val sheetState = rememberStandardBottomSheetState(
        initialValue = SheetValue.PartiallyExpanded,
        skipHiddenState = false,
    )
    val scaffoldState = rememberBottomSheetScaffoldState(bottomSheetState = sheetState)

    BottomSheetScaffold(
        sheetContent = {
            Column(modifier = Modifier.padding(16.dp)) {
                Text("Bottom sheet content", style = MaterialTheme.typography.titleMedium)
            }
        },
        scaffoldState = scaffoldState,
        sheetPeekHeight = 56.dp,
    ) { innerPadding ->
        Content(modifier = Modifier.padding(innerPadding))
    }
}
@Composable
fun DropdownMenuExample() {
    var expanded by remember { mutableStateOf(false) }
    var selectedOption by remember { mutableStateOf("Select") }

    Box {
        OutlinedTextField(
            value = selectedOption,
            onValueChange = {},
            readOnly = true,
            label = { Text("Option") },
            trailingIcon = { Icon(Icons.Default.ArrowDropDown, null) },
            modifier = Modifier
                .fillMaxWidth()
                .menuAnchor()
                .clickable { expanded = true }
        )

        DropdownMenu(
            expanded = expanded,
            onDismissRequest = { expanded = false }
        ) {
            DropdownMenuItem(
                text = { Text("Option 1") },
                onClick = {
                    selectedOption = "Option 1"
                    expanded = false
                }
            )
            DropdownMenuItem(
                text = { Text("Option 2") },
                onClick = {
                    selectedOption = "Option 2"
                    expanded = false
                }
            )
            Divider()
            DropdownMenuItem(
                text = { Text("Option 3") },
                onClick = {
                    selectedOption = "Option 3"
                    expanded = false
                }
            )
        }
    }
}

// Icon button context menu
@Composable
fun OverflowMenu() {
    var expanded by remember { mutableStateOf(false) }

    Box {
        IconButton(onClick = { expanded = true }) {
            Icon(Icons.Default.MoreVert, "More")
        }
        DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
            DropdownMenuItem(
                text = { Text("Settings") },
                onClick = { /* ... */ },
                leadingIcon = { Icon(Icons.Default.Settings, null) }
            )
            DropdownMenuItem(
                text = { Text("Help") },
                onClick = { /* ... */ },
                leadingIcon = { Icon(Icons.Default.Help, null) }
            )
        }
    }
}

Toggle Buttons

// Segmented button (exclusive selection)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SegmentedButtonExample() {
    var selected by remember { mutableStateOf(0) }
    val options = listOf("Day", "Week", "Month")

    SingleChoiceSegmentedButtonRow {
        options.forEachIndexed { index, label ->
            SegmentedButton(
                selected = index == selected,
                onClick = { selected = index },
                shape = SegmentedButtonShape(index, options.size),
            ) {
                Text(label)
            }
        }
    }
}

// Icon toggle button
var bookmarked by remember { mutableStateOf(false) }
IconToggleButton(checked = bookmarked, onCheckedChange = { bookmarked = it }) {
    Icon(
        if (bookmarked) Icons.Default.Favorite else Icons.Default.FavoriteBorder,
        contentDescription = "Favorite",
        tint = if (bookmarked) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant
    )
}

Slider and Range Slider

// Continuous slider
var value by remember { mutableStateOf(0.5f) }
Slider(
    value = value,
    onValueChange = { value = it },
    valueRange = 0f..1f,
    steps = 4,  // 5 stops: 0.0, 0.25, 0.5, 0.75, 1.0
)

// Discrete slider with steps
var stepValue by remember { mutableStateOf(0f) }
Slider(
    value = stepValue,
    onValueChange = { stepValue = it },
    valueRange = 0f..100f,
    steps = 9,  // 10 stops
)

// Range slider
var range by remember { mutableStateOf(25f..75f) }
RangeSlider(
    value = range,
    onValueChange = { range = it },
    valueRange = 0f..100f,
)

Badges

// Badge on icon
BadgedBox(
    badge = {
        Badge { Text("3") }
    }
) {
    Icon(Icons.Default.Mail, contentDescription = "Mail")
}

// Dot badge (no number)
BadgedBox(
    badge = { Badge }
) {
    Icon(Icons.Default.Notifications, contentDescription = "Notifications")
}

// Badge on navigation item
NavigationBarItem(
    selected = false,
    onClick = { /* ... */ },
    icon = {
        BadgedBox(badge = { Badge { Text("5") } }) {
            Icon(Icons.Default.Mail, contentDescription = null)
        }
    },
    label = { Text("Inbox") },
)

Tooltip

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TooltipExample() {
    // Plain tooltip
    Box {
        val tooltipState = rememberTooltipState()
        TooltipBox(
            positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
            tooltip = { PlainTooltip { Text("Add new item") } },
            state = tooltipState,
        ) {
            IconButton(onClick = { /* add */ }) {
                Icon(Icons.Default.Add, contentDescription = "Add")
            }
        }
    }

    // Rich tooltip (with subtitle and action)
    Box {
        val richState = rememberTooltipState()
        TooltipBox(
            positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
            tooltip = {
                RichTooltip(
                    title = { Text("Format") },
                    action = {
                        TextButton(onClick = { /* learn more */ }) { Text("Learn more") }
                    }
                ) {
                    Text("Bold, italic, and underline formatting options.")
                }
            },
            state = richState,
        ) {
            IconButton(onClick = { /* format */ }) {
                Icon(Icons.Default.FormatBold, contentDescription = "Format")
            }
        }
    }
}

Divider

// Horizontal divider
HorizontalDivider()

// With thickness and color
HorizontalDivider(
    thickness = 2.dp,
    color = MaterialTheme.colorScheme.outlineVariant,
)

// Vertical divider
VerticalDivider(modifier = Modifier.height(24.dp))

// Divider with text
Row(verticalAlignment = Alignment.CenterHorizontally, modifier = Modifier.fillMaxWidth()) {
    HorizontalDivider(modifier = Modifier.weight(1f))
    Text("  OR  ", style = MaterialTheme.typography.labelSmall)
    HorizontalDivider(modifier = Modifier.weight(1f))
}

SwipeToDismiss

@Composable
fun SwipeToDismissList(items: List<String>, onDismiss: (String) -> Unit) {
    LazyColumn {
        items(items, key = { it }) { item ->
            val state = rememberSwipeToDismissBoxState(
                confirmValueChange = { value ->
                    if (value == SwipeToDismissBoxValue.EndToStart) {
                        onDismiss(item)
                        true
                    } else false
                },
                positionalThreshold = { totalDistance -> totalDistance * 0.5f },
            )

            SwipeToDismissBox(
                state = state,
                backgroundContent = {
                    val color = when {
                        state.progress > 0 -> MaterialTheme.colorScheme.errorContainer
                        else -> Color.Transparent
                    }
                    Box(
                        modifier = Modifier
                            .fillMaxSize()
                            .background(color)
                            .padding(horizontal = 20.dp),
                        contentAlignment = Alignment.CenterEnd,
                    ) {
                        Icon(Icons.Default.Delete, "Delete", tint = MaterialTheme.colorScheme.onErrorContainer)
                    }
                },
                enableDismissFromStartToEnd = false,
            ) {
                Card(modifier = Modifier.fillMaxWidth()) {
                    Text(item, modifier = Modifier.padding(16.dp))
                }
            }
        }
    }
}

PullToRefresh

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PullToRefreshExample(viewModel: MyViewModel) {
    val isRefreshing by viewModel.isRefreshing.collectAsStateWithLifecycle()

    PullToRefreshBox(
        isRefreshing = isRefreshing,
        onRefresh = { viewModel.refresh() },
    ) {
        LazyColumn {
            items(viewModel.items) { item ->
                ItemRow(item)
            }
        }
    }
}

Full Scaffold example

Putting it all together — a complete screen with top bar, bottom navigation, FAB, and snackbar:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CompleteScreen() {
    var selectedTab by remember { mutableStateOf(0) }
    val snackbarHostState = remember { SnackbarHostState() }
    val scope = rememberCoroutineScope()
    val drawerState = rememberDrawerState(DrawerValue.Closed)

    ModalNavigationDrawer(
        drawerContent = {
            ModalDrawerSheet {
                Text("Menu", modifier = Modifier.padding(16.dp), style = MaterialTheme.typography.titleMedium)
                HorizontalDivider()
                NavigationDrawerItem(
                    label = { Text("Home") },
                    selected = selectedTab == 0,
                    onClick = { selectedTab = 0; scope.launch { drawerState.close() } },
                    icon = { Icon(Icons.Default.Home, null) },
                )
                NavigationDrawerItem(
                    label = { Text("Settings") },
                    selected = false,
                    onClick = { scope.launch { drawerState.close() } },
                    icon = { Icon(Icons.Default.Settings, null) },
                )
            }
        },
        drawerState = drawerState,
    ) {
        Scaffold(
            topBar = {
                TopAppBar(
                    title = { Text("My App") },
                    navigationIcon = {
                        IconButton(onClick = { scope.launch { drawerState.open() } }) {
                            Icon(Icons.Default.Menu, "Menu")
                        }
                    },
                    actions = {
                        IconButton(onClick = { /* search */ }) {
                            Icon(Icons.Default.Search, "Search")
                        }
                    },
                )
            },
            bottomBar = {
                NavigationBar {
                    NavigationBarItem(selected = selectedTab == 0, onClick = { selectedTab = 0 },
                        icon = { Icon(Icons.Default.Home, null) }, label = { Text("Home") })
                    NavigationBarItem(selected = selectedTab == 1, onClick = { selectedTab = 1 },
                        icon = { Icon(Icons.Default.Search, null) }, label = { Text("Search") })
                    NavigationBarItem(selected = selectedTab == 2, onClick = { selectedTab = 2 },
                        icon = { Icon(Icons.Default.Person, null) }, label = { Text("Profile") })
                }
            },
            floatingActionButton = {
                FloatingActionButton(onClick = {
                    scope.launch {
                        snackbarHostState.showSnackbar("Item added")
                    }
                }) {
                    Icon(Icons.Default.Add, "Add")
                }
            },
            snackbarHost = { SnackbarHost(snackbarHostState) },
        ) { innerPadding ->
            when (selectedTab) {
                0 -> HomeTab(modifier = Modifier.padding(innerPadding))
                1 -> SearchTab(modifier = Modifier.padding(innerPadding))
                2 -> ProfileTab(modifier = Modifier.padding(innerPadding))
            }
        }
    }
}

Component quick reference

Component Use for
Card / ElevatedCard / OutlinedCard Grouped content with actions
AlertDialog Confirmation, alerts, choices
DatePicker / TimePicker Date and time selection
Snackbar / SnackbarHost Brief messages with optional action
FilterChip / InputChip / SuggestionChip / AssistChip Tags, filters, suggestions
TabRow / ScrollableTabRow / SecondaryTabRow Tab navigation
TopAppBar / CenterAlignedTopAppBar / MediumTopAppBar / LargeTopAppBar Screen headers
NavigationBar / NavigationRail / NavigationDrawer App-level navigation
FloatingActionButton / ExtendedFloatingActionButton Primary action
ModalBottomSheet / BottomSheetScaffold Bottom-anchored panels
DropdownMenu Context menus, overflow menus
SegmentedButton Mutually exclusive options
IconToggleButton Toggle icons (favorite, bookmark)
Slider / RangeSlider Continuous value selection
Badge / BadgedBox Notification counts, status dots
Tooltip / PlainTooltip / RichTooltip Descriptive hover text
HorizontalDivider / VerticalDivider Visual separators
SwipeToDismissBox Swipe-to-delete list items
PullToRefreshBox Pull-to-refresh gesture
Scaffold Full screen layout (top bar, bottom bar, FAB, snackbar)