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
name | description |
---|---|
expanded | whether the FloatingToolbar is in expanded mode, i.e. showing [leadingContent] and [trailingContent]. |
modifier | the [Modifier] to be applied to this FloatingToolbar. |
colors | the colors used for this floating toolbar. There are two predefined [FloatingToolbarColors] at [FloatingToolbarDefaults.standardFloatingToolbarColors] and [FloatingToolbarDefaults.vibrantFloatingToolbarColors] which you can use or modify. |
contentPadding | the padding applied to the content of this FloatingToolbar. |
scrollBehavior | a [FloatingToolbarScrollBehavior]. If null, this FloatingToolbar will not automatically react to scrolling. |
shape | the shape used for this FloatingToolbar. |
leadingContent | the 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. |
trailingContent | the 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. |
content | the 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
name | description |
---|---|
expanded | whether the FloatingToolbar is in expanded mode, i.e. showing [leadingContent] and [trailingContent]. |
modifier | the [Modifier] to be applied to this FloatingToolbar. |
containerColor | the color used for the background of this FloatingToolbar. Use [Color.Transparent] to have no color. |
contentPadding | the padding applied to the content of this FloatingToolbar. |
scrollBehavior | a [FloatingToolbarScrollBehavior]. If null, this FloatingToolbar will not automatically react to scrolling. |
shape | the shape used for this FloatingToolbar. |
leadingContent | the 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. |
trailingContent | the 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. |
content | the 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
name | description |
---|---|
expanded | whether 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. |
floatingActionButton | a 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]. |
modifier | the [Modifier] to be applied to this floating toolbar. |
colors | the 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. |
contentPadding | the padding applied to the content of this floating toolbar. |
scrollBehavior | a [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. |
shape | the shape used for this floating toolbar content. |
floatingActionButtonPosition | the 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). |
animationSpec | the animation spec to use for this floating toolbar expand and collapse animation. |
content | the 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() })
}
}
}
}