Build apps faster with over 150+ styled components and screens! Check it out →

HorizontalFloatingToolbar

Common

Component in Material 3 Compose

A horizontal floating toolbar displays navigation and key actions in a [Row]. It can be positioned anywhere on the screen and floats over the rest of the content.

Last updated:

Installation

dependencies {
   implementation("androidx.compose.material3:material3:1.4.0-alpha07")
}

Overloads

@ExperimentalMaterial3ExpressiveApi
@Composable
fun HorizontalFloatingToolbar(
    expanded: Boolean,
    modifier: Modifier = Modifier,
    colors: FloatingToolbarColors = FloatingToolbarDefaults.standardFloatingToolbarColors(),
    contentPadding: PaddingValues = FloatingToolbarDefaults.ContentPadding,
    scrollBehavior: FloatingToolbarScrollBehavior? = null,
    shape: Shape = FloatingToolbarDefaults.ContainerShape,
    leadingContent: @Composable (RowScope.() -> Unit)? = null,
    trailingContent: @Composable (RowScope.() -> Unit)? = null,
    content: @Composable RowScope.() -> Unit
)

Parameters

namedescription
expandedwhether the FloatingToolbar is in expanded mode, i.e. showing [leadingContent] and [trailingContent].
modifierthe [Modifier] to be applied to this FloatingToolbar.
colorsthe colors used for this floating toolbar. There are two predefined [FloatingToolbarColors] at [FloatingToolbarDefaults.standardFloatingToolbarColors] and [FloatingToolbarDefaults.vibrantFloatingToolbarColors] which you can use or modify.
contentPaddingthe padding applied to the content of this FloatingToolbar.
scrollBehaviora [FloatingToolbarScrollBehavior]. If null, this FloatingToolbar will not automatically react to scrolling.
shapethe shape used for this FloatingToolbar.
leadingContentthe leading content of this FloatingToolbar. The default layout here is a [Row], so content inside will be placed horizontally. Only showing if [expanded] is true.
trailingContentthe trailing content of this FloatingToolbar. The default layout here is a [Row], so content inside will be placed horizontally. Only showing if [expanded] is true.
contentthe main content of this FloatingToolbar. The default layout here is a [Row], so content inside will be placed horizontally.
@Deprecated(
    message = "Use the HorizontalFloatingToolbar function that accepts FloatingToolbarColors",
    level = DeprecationLevel.HIDDEN
)
@ExperimentalMaterial3ExpressiveApi
@Composable
fun HorizontalFloatingToolbar(
    expanded: Boolean,
    modifier: Modifier = Modifier,
    containerColor: Color = ColorSchemeKeyTokens.PrimaryContainer.value,
    contentPadding: PaddingValues = FloatingToolbarDefaults.ContentPadding,
    scrollBehavior: FloatingToolbarScrollBehavior? = null,
    shape: Shape = FloatingToolbarDefaults.ContainerShape,
    leadingContent: @Composable (RowScope.() -> Unit)? = null,
    trailingContent: @Composable (RowScope.() -> Unit)? = null,
    content: @Composable RowScope.() -> Unit
)

Parameters

namedescription
expandedwhether the FloatingToolbar is in expanded mode, i.e. showing [leadingContent] and [trailingContent].
modifierthe [Modifier] to be applied to this FloatingToolbar.
containerColorthe color used for the background of this FloatingToolbar. Use [Color.Transparent] to have no color.
contentPaddingthe padding applied to the content of this FloatingToolbar.
scrollBehaviora [FloatingToolbarScrollBehavior]. If null, this FloatingToolbar will not automatically react to scrolling.
shapethe shape used for this FloatingToolbar.
leadingContentthe leading content of this FloatingToolbar. The default layout here is a [Row], so content inside will be placed horizontally. Only showing if [expanded] is true.
trailingContentthe trailing content of this FloatingToolbar. The default layout here is a [Row], so content inside will be placed horizontally. Only showing if [expanded] is true.
contentthe main content of this FloatingToolbar. The default layout here is a [Row], so content inside will be placed horizontally.
@ExperimentalMaterial3ExpressiveApi
@Composable
fun HorizontalFloatingToolbar(
    expanded: Boolean,
    floatingActionButton: @Composable () -> Unit,
    modifier: Modifier = Modifier,
    colors: FloatingToolbarColors = FloatingToolbarDefaults.standardFloatingToolbarColors(),
    contentPadding: PaddingValues = FloatingToolbarDefaults.ContentPadding,
    scrollBehavior: FloatingToolbarScrollBehavior? = null,
    shape: Shape = FloatingToolbarDefaults.ContainerShape,
    floatingActionButtonPosition: FloatingToolbarHorizontalFabPosition =
        FloatingToolbarHorizontalFabPosition.End,
    animationSpec: FiniteAnimationSpec<Float> = FloatingToolbarDefaults.animationSpec(),
    content: @Composable RowScope.() -> Unit
)

Parameters

namedescription
expandedwhether the floating toolbar is expanded or not. In its expanded state, the FAB and the toolbar content are organized horizontally. Otherwise, only the FAB is visible.
floatingActionButtona floating action button to be displayed by the toolbar. It's recommended to use a [FloatingToolbarDefaults.VibrantFloatingActionButton] or [FloatingToolbarDefaults.StandardFloatingActionButton] that is styled to match the [colors].
modifierthe [Modifier] to be applied to this floating toolbar.
colorsthe colors used for this floating toolbar. There are two predefined [FloatingToolbarColors] at [FloatingToolbarDefaults.standardFloatingToolbarColors] and [FloatingToolbarDefaults.vibrantFloatingToolbarColors] which you can use or modify. See also [floatingActionButton] for more information on the right FAB to use for proper styling.
contentPaddingthe padding applied to the content of this floating toolbar.
scrollBehaviora [FloatingToolbarScrollBehavior]. If provided, this FloatingToolbar will automatically react to scrolling. If your toolbar is positioned along a center edge of the screen (like top or bottom center), it's best to use this scroll behavior to make the entire toolbar scroll off-screen as the user scrolls. This would prevent the FAB from appearing off-center, which may occur in this case when using the [expanded] flag to simply expand or collapse the toolbar.
shapethe shape used for this floating toolbar content.
floatingActionButtonPositionthe position of the floating toolbar's floating action button. By default, the FAB is placed at the end of the toolbar (i.e. aligned to the right in left-to-right layout, or to the left in right-to-left layout).
animationSpecthe animation spec to use for this floating toolbar expand and collapse animation.
contentthe main content of this floating toolbar. The default layout here is a [Row], so content inside will be placed horizontally.

Code Examples

ExpandableHorizontalFloatingToolbarSample

@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Preview
@Composable
fun ExpandableHorizontalFloatingToolbarSample() {
    val context = LocalContext.current
    val isTouchExplorationEnabled = remember {
        val am = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
        am.isEnabled && am.isTouchExplorationEnabled
    }
    val listState = rememberLazyListState()
    var currentItem = 0
    val expanded by remember {
        derivedStateOf {
            val temp = currentItem
            currentItem = listState.firstVisibleItemIndex
            listState.firstVisibleItemIndex {'<='} temp // true if the list is scrolled up
        }
    }
    Scaffold(
        content = { innerPadding ->
            Box(Modifier.padding(innerPadding)) {
                LazyColumn(
                    state = listState,
                    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)
                        )
                    }
                }
                HorizontalFloatingToolbar(
                    modifier = Modifier.align(Alignment.BottomCenter).offset(y = -ScreenOffset),
                    expanded = expanded || isTouchExplorationEnabled,
                    leadingContent = { leadingContent() },
                    trailingContent = { trailingContent() },
                    content = {
                        FilledIconButton(
                            modifier = Modifier.width(64.dp),
                            onClick = { /* doSomething() */ }
                        ) {
                            Icon(Icons.Filled.Add, contentDescription = "Localized description")
                        }
                    },
                )
            }
        }
    )
}

ScrollableHorizontalFloatingToolbarSample

@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Preview
@Composable
fun ScrollableHorizontalFloatingToolbarSample() {
    val context = LocalContext.current
    val isTouchExplorationEnabled = remember {
        val am = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
        am.isEnabled && am.isTouchExplorationEnabled
    }
    val listState = rememberLazyListState()
    val exitAlwaysScrollBehavior =
        FloatingToolbarDefaults.exitAlwaysScrollBehavior(exitDirection = Bottom)
    Scaffold(
        modifier = Modifier.nestedScroll(exitAlwaysScrollBehavior),
        content = { innerPadding ->
            Box(Modifier.padding(innerPadding)) {
                LazyColumn(
                    state = listState,
                    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)
                        )
                    }
                }
                HorizontalFloatingToolbar(
                    modifier = Modifier.align(Alignment.BottomCenter).offset(y = -ScreenOffset),
                    expanded = true,
                    leadingContent = { leadingContent() },
                    trailingContent = { trailingContent() },
                    content = {
                        FilledIconButton(
                            modifier = Modifier.width(64.dp),
                            onClick = { /* doSomething() */ }
                        ) {
                            Icon(Icons.Filled.Add, contentDescription = "Localized description")
                        }
                    },
                    scrollBehavior =
                        if (!isTouchExplorationEnabled) exitAlwaysScrollBehavior else null,
                )
            }
        }
    )
}

HorizontalFloatingToolbarWithFabSample

@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Preview
@Composable
fun HorizontalFloatingToolbarWithFabSample() {
    val context = LocalContext.current
    val isTouchExplorationEnabled = remember {
        val am = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
        am.isEnabled && am.isTouchExplorationEnabled
    }

    var expanded by rememberSaveable { mutableStateOf(true) }
    val vibrantColors = FloatingToolbarDefaults.vibrantFloatingToolbarColors()
    Scaffold { 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. We don't intercept
                    // scrolls if the touch exploration is enabled (i.e. Talkback).
                    .then(
                        if (!isTouchExplorationEnabled) {
                            Modifier.floatingToolbarVerticalNestedScroll(
                                expanded = expanded,
                                onExpand = { expanded = true },
                                onCollapse = { expanded = false }
                            )
                        } else {
                            Modifier
                        }
                    )
                    .verticalScroll(rememberScrollState())
            ) {
                Text(text = remember { LoremIpsum().values.first() })
            }
            HorizontalFloatingToolbar(
                expanded = expanded,
                floatingActionButton = {
                    // Match the FAB to the vibrantColors. See also StandardFloatingActionButton.
                    FloatingToolbarDefaults.VibrantFloatingActionButton(
                        onClick = { /* doSomething() */ },
                    ) {
                        Icon(Icons.Filled.Add, "Localized description")
                    }
                },
                modifier =
                    Modifier.align(Alignment.BottomEnd)
                        .offset(x = -ScreenOffset, y = -ScreenOffset),
                colors = vibrantColors,
                content = {
                    IconButton(onClick = { /* doSomething() */ }) {
                        Icon(Icons.Filled.Person, contentDescription = "Localized description")
                    }
                    IconButton(onClick = { /* doSomething() */ }) {
                        Icon(Icons.Filled.Edit, contentDescription = "Localized description")
                    }
                    IconButton(onClick = { /* doSomething() */ }) {
                        Icon(Icons.Filled.Favorite, contentDescription = "Localized description")
                    }
                    IconButton(onClick = { /* doSomething() */ }) {
                        Icon(Icons.Filled.MoreVert, contentDescription = "Localized description")
                    }
                },
            )
        }
    }
}

CenteredHorizontalFloatingToolbarWithFabSample

@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Preview
@Composable
fun CenteredHorizontalFloatingToolbarWithFabSample() {
    val context = LocalContext.current
    val isTouchExplorationEnabled = remember {
        val am = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
        am.isEnabled && am.isTouchExplorationEnabled
    }
    val exitAlwaysScrollBehavior =
        FloatingToolbarDefaults.exitAlwaysScrollBehavior(exitDirection = Bottom)
    val vibrantColors = FloatingToolbarDefaults.vibrantFloatingToolbarColors()
    Scaffold(modifier = Modifier.nestedScroll(exitAlwaysScrollBehavior)) { innerPadding ->
        Box(Modifier.padding(innerPadding)) {
            Column(
                Modifier.fillMaxWidth()
                    .padding(horizontal = 16.dp)
                    .verticalScroll(rememberScrollState())
            ) {
                Text(text = remember { LoremIpsum().values.first() })
            }
            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 = {
                    // 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),
                colors = vibrantColors,
                scrollBehavior = if (!isTouchExplorationEnabled) exitAlwaysScrollBehavior else null,
                content = {
                    IconButton(onClick = { /* doSomething() */ }) {
                        Icon(Icons.Filled.Person, contentDescription = "Localized description")
                    }
                    IconButton(onClick = { /* doSomething() */ }) {
                        Icon(Icons.Filled.Edit, contentDescription = "Localized description")
                    }
                    IconButton(onClick = { /* doSomething() */ }) {
                        Icon(Icons.Filled.Favorite, contentDescription = "Localized description")
                    }
                    IconButton(onClick = { /* doSomething() */ }) {
                        Icon(Icons.Filled.MoreVert, contentDescription = "Localized description")
                    }
                },
            )
        }
    }
}

HorizontalFloatingToolbarAsScaffoldFabSample

@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Preview
@Composable
fun HorizontalFloatingToolbarAsScaffoldFabSample() {
    val context = LocalContext.current
    val isTouchExplorationEnabled = remember {
        val am = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
        am.isEnabled && am.isTouchExplorationEnabled
    }

    var expanded by rememberSaveable { mutableStateOf(true) }
    val vibrantColors = FloatingToolbarDefaults.vibrantFloatingToolbarColors()
    Scaffold(
        floatingActionButton = {
            HorizontalFloatingToolbar(
                expanded = expanded,
                floatingActionButton = {
                    // Match the FAB to the vibrantColors. See also StandardFloatingActionButton.
                    FloatingToolbarDefaults.VibrantFloatingActionButton(
                        onClick = { expanded = !expanded },
                    ) {
                        Icon(Icons.Filled.Add, "Localized description")
                    }
                },
                colors = vibrantColors,
                content = {
                    IconButton(onClick = { /* doSomething() */ }) {
                        Icon(Icons.Filled.Person, contentDescription = "Localized description")
                    }
                    IconButton(onClick = { /* doSomething() */ }) {
                        Icon(Icons.Filled.Edit, contentDescription = "Localized description")
                    }
                    IconButton(onClick = { /* doSomething() */ }) {
                        Icon(Icons.Filled.Favorite, contentDescription = "Localized description")
                    }
                    IconButton(onClick = { /* doSomething() */ }) {
                        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. We don't intercept
                    // scrolls if the touch exploration is enabled (i.e. Talkback).
                    .then(
                        if (!isTouchExplorationEnabled) {
                            Modifier.floatingToolbarVerticalNestedScroll(
                                expanded = expanded,
                                onExpand = { expanded = true },
                                onCollapse = { expanded = false }
                            )
                        } else {
                            Modifier
                        }
                    )
                    .verticalScroll(rememberScrollState())
            ) {
                Text(text = remember { LoremIpsum().values.first() })
            }
        }
    }
}
by @alexstyl