Composable Component

HorizontalFloatingToolbar

A horizontal floating toolbar displays navigation and key actions in a Row.

CenteredHorizontalFloatingToolbarWithFabSample

@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class)
@Preview
@Composable
fun CenteredHorizontalFloatingToolbarWithFabSample() {
    val exitAlwaysScrollBehavior =
        FloatingToolbarDefaults.exitAlwaysScrollBehavior(exitDirection = Bottom)
    val vibrantColors = FloatingToolbarDefaults.vibrantFloatingToolbarColors()
    Scaffold(modifier = Modifier.nestedScroll(exitAlwaysScrollBehavior)) { innerPadding ->
        Box(Modifier.padding(innerPadding)) {
            // The toolbar should receive focus before the screen content for a11y, so place it
            // first. Make sure to set its zIndex so it's above the screen content visually.
            HorizontalFloatingToolbar(
                // Always expanded as the toolbar is bottom-centered. We will use a
                // FloatingToolbarScrollBehavior to hide both the toolbar and its FAB on scroll.
                expanded = true,
                floatingActionButton = {
                    TooltipBox(
                        positionProvider =
                            TooltipDefaults.rememberTooltipPositionProvider(
                                TooltipAnchorPosition.Above
                            ),
                        tooltip = { PlainTooltip { Text("Localized description") } },
                        state = rememberTooltipState(),
                    ) {
                        // Match the FAB to the vibrantColors. See also
                        // StandardFloatingActionButton.
                        FloatingToolbarDefaults.VibrantFloatingActionButton(
                            onClick = { /* doSomething() */ }
                        ) {
                            Icon(Icons.Filled.Add, "Localized description")
                        }
                    }
                },
                modifier =
                    Modifier.align(Alignment.BottomCenter).offset(y = -ScreenOffset).zIndex(1f),
                colors = vibrantColors,
                scrollBehavior = exitAlwaysScrollBehavior,
                content = {
                    TooltipBox(
                        positionProvider =
                            TooltipDefaults.rememberTooltipPositionProvider(
                                TooltipAnchorPosition.Above
                            ),
                        tooltip = { PlainTooltip { Text("Localized description") } },
                        state = rememberTooltipState(),
                    ) {
                        IconButton(onClick = { /* doSomething() */ }) {
                            Icon(Icons.Filled.Person, contentDescription = "Localized description")
                        }
                    }
                    TooltipBox(
                        positionProvider =
                            TooltipDefaults.rememberTooltipPositionProvider(
                                TooltipAnchorPosition.Above
                            ),
                        tooltip = { PlainTooltip { Text("Localized description") } },
                        state = rememberTooltipState(),
                    ) {
                        IconButton(onClick = { /* doSomething() */ }) {
                            Icon(Icons.Filled.Edit, contentDescription = "Localized description")
                        }
                    }
                    TooltipBox(
                        positionProvider =
                            TooltipDefaults.rememberTooltipPositionProvider(
                                TooltipAnchorPosition.Above
                            ),
                        tooltip = { PlainTooltip { Text("Localized description") } },
                        state = rememberTooltipState(),
                    ) {
                        IconButton(onClick = { /* doSomething() */ }) {
                            Icon(
                                Icons.Filled.Favorite,
                                contentDescription = "Localized description",
                            )
                        }
                    }
                    TooltipBox(
                        positionProvider =
                            TooltipDefaults.rememberTooltipPositionProvider(
                                TooltipAnchorPosition.Above
                            ),
                        tooltip = { PlainTooltip { Text("Localized description") } },
                        state = rememberTooltipState(),
                    ) {
                        IconButton(onClick = { /* doSomething() */ }) {
                            Icon(
                                Icons.Filled.MoreVert,
                                contentDescription = "Localized description",
                            )
                        }
                    }
                },
            )
            Column(
                Modifier.fillMaxWidth()
                    .padding(horizontal = 16.dp)
                    .verticalScroll(rememberScrollState())
            ) {
                Text(text = remember { LoremIpsum().values.first() })
            }
        }
    }
}

ExpandableHorizontalFloatingToolbarSample

@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class)
@Preview
@Composable
fun ExpandableHorizontalFloatingToolbarSample() {
    var expanded by rememberSaveable { mutableStateOf(true) }
    Scaffold(
        content = { innerPadding ->
            Box(Modifier.padding(innerPadding)) {
                // The toolbar should receive focus before the screen content, so place it first.
                // Make sure to set its zIndex so it's above the screen content visually.
                HorizontalFloatingToolbar(
                    modifier =
                        Modifier.align(Alignment.BottomCenter).offset(y = -ScreenOffset).zIndex(1f),
                    expanded = expanded,
                    leadingContent = { LeadingContent() },
                    trailingContent = { TrailingContent() },
                    content = {
                        TooltipBox(
                            positionProvider =
                                TooltipDefaults.rememberTooltipPositionProvider(
                                    TooltipAnchorPosition.Above
                                ),
                            tooltip = { PlainTooltip { Text("Localized description") } },
                            state = rememberTooltipState(),
                        ) {
                            FilledIconButton(
                                modifier = Modifier.width(64.dp),
                                onClick = { /* doSomething() */ },
                            ) {
                                Icon(Icons.Filled.Add, contentDescription = "Localized description")
                            }
                        }
                    },
                )
                LazyColumn(
                    // Apply a floatingToolbarVerticalNestedScroll Modifier toggle the expanded
                    // state of the HorizontalFloatingToolbar.
                    modifier =
                        Modifier.floatingToolbarVerticalNestedScroll(
                            expanded = expanded,
                            onExpand = { expanded = true },
                            onCollapse = { expanded = false },
                        ),
                    state = rememberLazyListState(),
                    contentPadding = innerPadding,
                    verticalArrangement = Arrangement.spacedBy(8.dp),
                ) {
                    val list = (0..75).map { it.toString() }
                    items(count = list.size) {
                        Text(
                            text = list[it],
                            style = MaterialTheme.typography.bodyLarge,
                            modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
                        )
                    }
                }
            }
        }
    )
}

HorizontalFloatingToolbarAsScaffoldFabSample

@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class)
@Preview
@Composable
fun HorizontalFloatingToolbarAsScaffoldFabSample() {
    var expanded by rememberSaveable { mutableStateOf(true) }
    val vibrantColors = FloatingToolbarDefaults.vibrantFloatingToolbarColors()
    Scaffold(
        floatingActionButton = {
            HorizontalFloatingToolbar(
                expanded = expanded,
                floatingActionButton = {
                    TooltipBox(
                        positionProvider =
                            TooltipDefaults.rememberTooltipPositionProvider(
                                TooltipAnchorPosition.Above
                            ),
                        tooltip = { PlainTooltip { Text("Localized description") } },
                        state = rememberTooltipState(),
                    ) {
                        // Match the FAB to the vibrantColors. See also
                        // StandardFloatingActionButton.
                        FloatingToolbarDefaults.VibrantFloatingActionButton(
                            onClick = { expanded = !expanded }
                        ) {
                            Icon(Icons.Filled.Add, "Localized description")
                        }
                    }
                },
                colors = vibrantColors,
                content = {
                    // Make sure the buttons are not focusable if they are not visible, so that
                    // keyboard focus doesn't go to an invisible element on the screen.
                    TooltipBox(
                        positionProvider =
                            TooltipDefaults.rememberTooltipPositionProvider(
                                TooltipAnchorPosition.Above
                            ),
                        tooltip = { PlainTooltip { Text("Localized description") } },
                        state = rememberTooltipState(),
                    ) {
                        IconButton(
                            onClick = { /* doSomething() */ },
                            Modifier.focusProperties { canFocus = expanded },
                        ) {
                            Icon(Icons.Filled.Person, contentDescription = "Localized description")
                        }
                    }
                    TooltipBox(
                        positionProvider =
                            TooltipDefaults.rememberTooltipPositionProvider(
                                TooltipAnchorPosition.Above
                            ),
                        tooltip = { PlainTooltip { Text("Localized description") } },
                        state = rememberTooltipState(),
                    ) {
                        IconButton(
                            onClick = { /* doSomething() */ },
                            Modifier.focusProperties { canFocus = expanded },
                        ) {
                            Icon(Icons.Filled.Edit, contentDescription = "Localized description")
                        }
                    }
                    TooltipBox(
                        positionProvider =
                            TooltipDefaults.rememberTooltipPositionProvider(
                                TooltipAnchorPosition.Above
                            ),
                        tooltip = { PlainTooltip { Text("Localized description") } },
                        state = rememberTooltipState(),
                    ) {
                        IconButton(
                            onClick = { /* doSomething() */ },
                            Modifier.focusProperties { canFocus = expanded },
                        ) {
                            Icon(
                                Icons.Filled.Favorite,
                                contentDescription = "Localized description",
                            )
                        }
                    }
                    TooltipBox(
                        positionProvider =
                            TooltipDefaults.rememberTooltipPositionProvider(
                                TooltipAnchorPosition.Above
                            ),
                        tooltip = { PlainTooltip { Text("Localized description") } },
                        state = rememberTooltipState(),
                    ) {
                        IconButton(
                            onClick = { /* doSomething() */ },
                            Modifier.focusProperties { canFocus = expanded },
                        ) {
                            Icon(
                                Icons.Filled.MoreVert,
                                contentDescription = "Localized description",
                            )
                        }
                    }
                },
            )
        },
        // When setting this to `FabPosition.Start` remember to set a
        // `floatingActionButtonPosition = FloatingToolbarHorizontalFabPosition.Start` at the
        // HorizontalFloatingToolbar as well.
        floatingActionButtonPosition = FabPosition.End,
    ) { innerPadding ->
        Box(Modifier.padding(innerPadding)) {
            Column(
                Modifier.fillMaxWidth()
                    .padding(horizontal = 16.dp)
                    // Apply a floatingToolbarVerticalNestedScroll Modifier to the Column to toggle
                    // the expanded state of the HorizontalFloatingToolbar.
                    .then(
                        Modifier.floatingToolbarVerticalNestedScroll(
                            expanded = expanded,
                            onExpand = { expanded = true },
                            onCollapse = { expanded = false },
                        )
                    )
                    .verticalScroll(rememberScrollState())
            ) {
                Text(text = remember { LoremIpsum().values.first() })
            }
        }
    }
}

HorizontalFloatingToolbarWithFabSample

@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class)
@Preview
@Composable
fun HorizontalFloatingToolbarWithFabSample() {
    var expanded by rememberSaveable { mutableStateOf(true) }
    val vibrantColors = FloatingToolbarDefaults.vibrantFloatingToolbarColors()
    Scaffold { innerPadding ->
        Box(Modifier.padding(innerPadding)) {
            // The toolbar should receive focus before the screen content for a11y, so place it
            // first. Make sure to set its zIndex so it's above the screen content visually.
            HorizontalFloatingToolbar(
                expanded = expanded,
                floatingActionButton = {
                    TooltipBox(
                        positionProvider =
                            TooltipDefaults.rememberTooltipPositionProvider(
                                TooltipAnchorPosition.Above
                            ),
                        tooltip = { PlainTooltip { Text("Localized description") } },
                        state = rememberTooltipState(),
                    ) {
                        // Match the FAB to the vibrantColors. See also
                        // StandardFloatingActionButton.
                        FloatingToolbarDefaults.VibrantFloatingActionButton(
                            onClick = { expanded = !expanded }
                        ) {
                            Icon(Icons.Filled.Add, "Localized description")
                        }
                    }
                },
                modifier =
                    Modifier.align(Alignment.BottomEnd)
                        .offset(x = -ScreenOffset, y = -ScreenOffset)
                        .zIndex(1f),
                colors = vibrantColors,
                content = {
                    // Make sure the buttons are not focusable if they are not visible, so that
                    // keyboard focus doesn't go to an invisible element on the screen.
                    TooltipBox(
                        positionProvider =
                            TooltipDefaults.rememberTooltipPositionProvider(
                                TooltipAnchorPosition.Above
                            ),
                        tooltip = { PlainTooltip { Text("Localized description") } },
                        state = rememberTooltipState(),
                    ) {
                        IconButton(
                            onClick = { /* doSomething() */ },
                            Modifier.focusProperties { canFocus = expanded },
                        ) {
                            Icon(Icons.Filled.Person, contentDescription = "Localized description")
                        }
                    }
                    TooltipBox(
                        positionProvider =
                            TooltipDefaults.rememberTooltipPositionProvider(
                                TooltipAnchorPosition.Above
                            ),
                        tooltip = { PlainTooltip { Text("Localized description") } },
                        state = rememberTooltipState(),
                    ) {
                        IconButton(
                            onClick = { /* doSomething() */ },
                            Modifier.focusProperties { canFocus = expanded },
                        ) {
                            Icon(Icons.Filled.Edit, contentDescription = "Localized description")
                        }
                    }
                    TooltipBox(
                        positionProvider =
                            TooltipDefaults.rememberTooltipPositionProvider(
                                TooltipAnchorPosition.Above
                            ),
                        tooltip = { PlainTooltip { Text("Localized description") } },
                        state = rememberTooltipState(),
                    ) {
                        IconButton(
                            onClick = { /* doSomething() */ },
                            Modifier.focusProperties { canFocus = expanded },
                        ) {
                            Icon(
                                Icons.Filled.Favorite,
                                contentDescription = "Localized description",
                            )
                        }
                    }
                    TooltipBox(
                        positionProvider =
                            TooltipDefaults.rememberTooltipPositionProvider(
                                TooltipAnchorPosition.Above
                            ),
                        tooltip = { PlainTooltip { Text("Localized description") } },
                        state = rememberTooltipState(),
                    ) {
                        IconButton(
                            onClick = { /* doSomething() */ },
                            Modifier.focusProperties { canFocus = expanded },
                        ) {
                            Icon(
                                Icons.Filled.MoreVert,
                                contentDescription = "Localized description",
                            )
                        }
                    }
                },
            )
            Column(
                Modifier.fillMaxWidth()
                    .padding(horizontal = 16.dp)
                    // Apply a floatingToolbarVerticalNestedScroll Modifier to the Column to toggle
                    // the expanded state of the HorizontalFloatingToolbar.
                    .floatingToolbarVerticalNestedScroll(
                        expanded = expanded,
                        onExpand = { expanded = true },
                        onCollapse = { expanded = false },
                    )
                    .verticalScroll(rememberScrollState())
            ) {
                Text(text = remember { LoremIpsum().values.first() })
            }
        }
    }
}

OverflowingHorizontalFloatingToolbarSample

@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class)
@Preview
@Composable
fun OverflowingHorizontalFloatingToolbarSample() {
    Scaffold(
        content = { innerPadding ->
            Box(Modifier.padding(innerPadding)) {
                // The toolbar should receive focus before the screen content, so place it first.
                // Make sure to set its zIndex so it's above the screen content visually.
                HorizontalFloatingToolbar(
                    modifier =
                        Modifier.align(Alignment.BottomCenter).offset(y = -ScreenOffset).zIndex(1f),
                    expanded = true,
                    leadingContent = { LeadingContent() },
                    trailingContent = {
                        AppBarRow {
                            clickableItem(
                                onClick = { /* doSomething() */ },
                                icon = {
                                    Icon(
                                        Icons.Filled.Download,
                                        contentDescription = "Localized description",
                                    )
                                },
                                label = "Download",
                            )
                            clickableItem(
                                onClick = { /* doSomething() */ },
                                icon = {
                                    Icon(
                                        Icons.Filled.Favorite,
                                        contentDescription = "Localized description",
                                    )
                                },
                                label = "Favorite",
                            )
                            clickableItem(
                                onClick = { /* doSomething() */ },
                                icon = {
                                    Icon(
                                        Icons.Filled.Add,
                                        contentDescription = "Localized description",
                                    )
                                },
                                label = "Add",
                            )
                            clickableItem(
                                onClick = { /* doSomething() */ },
                                icon = {
                                    Icon(
                                        Icons.Filled.Person,
                                        contentDescription = "Localized description",
                                    )
                                },
                                label = "Person",
                            )
                            clickableItem(
                                onClick = { /* doSomething() */ },
                                icon = {
                                    Icon(
                                        Icons.Filled.ArrowUpward,
                                        contentDescription = "Localized description",
                                    )
                                },
                                label = "ArrowUpward",
                            )
                        }
                    },
                    content = {
                        TooltipBox(
                            positionProvider =
                                TooltipDefaults.rememberTooltipPositionProvider(
                                    TooltipAnchorPosition.Above
                                ),
                            tooltip = { PlainTooltip { Text("Localized description") } },
                            state = rememberTooltipState(),
                        ) {
                            FilledIconButton(
                                modifier = Modifier.width(64.dp),
                                onClick = { /* doSomething() */ },
                            ) {
                                Icon(Icons.Filled.Add, contentDescription = "Localized description")
                            }
                        }
                    },
                )
                LazyColumn(
                    state = rememberLazyListState(),
                    contentPadding = innerPadding,
                    verticalArrangement = Arrangement.spacedBy(8.dp),
                ) {
                    val list = (0..75).map { it.toString() }
                    items(count = list.size) {
                        Text(
                            text = list[it],
                            style = MaterialTheme.typography.bodyLarge,
                            modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
                        )
                    }
                }
            }
        }
    )
}

ScrollableHorizontalFloatingToolbarSample

@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class)
@Preview
@Composable
fun ScrollableHorizontalFloatingToolbarSample() {
    val exitAlwaysScrollBehavior =
        FloatingToolbarDefaults.exitAlwaysScrollBehavior(exitDirection = Bottom)
    Scaffold(
        modifier = Modifier.nestedScroll(exitAlwaysScrollBehavior),
        content = { innerPadding ->
            Box(Modifier.padding(innerPadding)) {
                // The toolbar should receive focus before the screen content, so place it first.
                // Make sure to set its zIndex so it's above the screen content visually.
                HorizontalFloatingToolbar(
                    modifier =
                        Modifier.align(Alignment.BottomCenter).offset(y = -ScreenOffset).zIndex(1f),
                    expanded = true,
                    leadingContent = { LeadingContent() },
                    trailingContent = { TrailingContent() },
                    content = {
                        TooltipBox(
                            positionProvider =
                                TooltipDefaults.rememberTooltipPositionProvider(
                                    TooltipAnchorPosition.Above
                                ),
                            tooltip = { PlainTooltip { Text("Localized description") } },
                            state = rememberTooltipState(),
                        ) {
                            FilledIconButton(
                                modifier = Modifier.width(64.dp),
                                onClick = { /* doSomething() */ },
                            ) {
                                Icon(Icons.Filled.Add, contentDescription = "Localized description")
                            }
                        }
                    },
                    scrollBehavior = exitAlwaysScrollBehavior,
                )
                LazyColumn(
                    state = rememberLazyListState(),
                    contentPadding = innerPadding,
                    verticalArrangement = Arrangement.spacedBy(8.dp),
                ) {
                    val list = (0..75).map { it.toString() }
                    items(count = list.size) {
                        Text(
                            text = list[it],
                            style = MaterialTheme.typography.bodyLarge,
                            modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
                        )
                    }
                }
            }
        },
    )
}