FlexibleBottomAppBar
Common
Component in Material 3 Compose
A flexible bottom app bar displays navigation and key actions at the bottom of small screens.
This variation of the Bottom app bar has a [horizontalArrangement] parameter for controlling the way the content is arranged. Also, it allows more flexibility in controlling the bar's expanded height with an [expandedHeight] value.
If you are interested in displaying a [FloatingActionButton], consider using another overload that takes a [FloatingActionButton] parameter.
Also see [NavigationBar].
Last updated:
Installation
dependencies {
implementation("androidx.compose.material3:material3:1.4.0-alpha07")
}
Overloads
@OptIn(ExperimentalMaterial3Api::class)
@ExperimentalMaterial3ExpressiveApi
@Composable
fun FlexibleBottomAppBar(
modifier: Modifier = Modifier,
containerColor: Color = BottomAppBarDefaults.containerColor,
contentColor: Color = contentColorFor(containerColor),
contentPadding: PaddingValues = BottomAppBarDefaults.FlexibleContentPadding,
horizontalArrangement: Arrangement.Horizontal =
BottomAppBarDefaults.FlexibleHorizontalArrangement,
expandedHeight: Dp = BottomAppBarDefaults.FlexibleBottomAppBarHeight,
windowInsets: WindowInsets = BottomAppBarDefaults.windowInsets,
scrollBehavior: BottomAppBarScrollBehavior? = null,
content: @Composable RowScope.() -> Unit
)
Parameters
name | description |
---|---|
modifier | the [Modifier] to be applied to this BottomAppBar |
containerColor | the color used for the background of this BottomAppBar. Use [Color.Transparent] to have no color. |
contentColor | the preferred color for content inside this BottomAppBar. Defaults to either the matching content color for [containerColor], or to the current [LocalContentColor] if [containerColor] is not a color from the theme. |
contentPadding | the padding applied to the content of this BottomAppBar |
horizontalArrangement | the horizontal arrangement of the content inside this BottomAppBar |
expandedHeight | the maximum height this bottom bar can reach when fully expanded. If a [scrollBehavior] is provided, the bar might collapse or expand based on scrolling. In that case, this value sets the upper limit for the bar's height during expansion. This [Dp] value must be specified, finite, and greater than zero; otherwise, [BottomAppBarDefaults.FlexibleBottomAppBarHeight] will be used as a default. In case the [scrollBehavior] is null , this value will simply be the fixed height of the bottom bar. |
windowInsets | a window insets that app bar will respect. |
scrollBehavior | a [BottomAppBarScrollBehavior] which holds various offset values that will be applied by this bottom app bar to set up its height. A scroll behavior is designed to work in conjunction with a scrolled content to change the bottom app bar appearance as the content scrolls. See [BottomAppBarScrollBehavior.nestedScrollConnection]. |
content | the content of this BottomAppBar. The default layout here is a [Row], so content inside will be placed horizontally. |
Code Examples
ExitAlwaysBottomAppBarSpacedAround
/**
* A sample for a [FlexibleBottomAppBar] that collapses when the content is scrolled up, and appears
* when the content scrolled down. The content is spaced around.
*/
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Preview
@Composable
fun ExitAlwaysBottomAppBarSpacedAround() {
val context = LocalContext.current
val isTouchExplorationEnabled = remember {
val am = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
am.isEnabled && am.isTouchExplorationEnabled
}
val scrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior()
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
bottomBar = {
FlexibleBottomAppBar(
horizontalArrangement = Arrangement.SpaceAround,
contentPadding = PaddingValues(horizontal = 0.dp),
scrollBehavior = if (!isTouchExplorationEnabled) scrollBehavior else null,
content = {
IconButton(onClick = { /* doSomething() */ }) {
Icon(
Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Localized description"
)
}
IconButton(onClick = { /* doSomething() */ }) {
Icon(
Icons.AutoMirrored.Filled.ArrowForward,
contentDescription = "Localized description"
)
}
FilledIconButton(
modifier = Modifier.width(56.dp),
onClick = { /* doSomething() */ }
) {
Icon(Icons.Filled.Add, contentDescription = "Localized description")
}
IconButton(onClick = { /* doSomething() */ }) {
Icon(Icons.Filled.Check, contentDescription = "Localized description")
}
IconButton(onClick = { /* doSomething() */ }) {
Icon(Icons.Filled.Edit, contentDescription = "Localized description")
}
}
)
},
content = { innerPadding ->
LazyColumn(
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)
)
}
}
}
)
}
ExitAlwaysBottomAppBarSpacedBetween
/**
* A sample for a [FlexibleBottomAppBar] that collapses when the content is scrolled up, and appears
* when the content scrolled down. The content is spaced between.
*/
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Preview
@Composable
fun ExitAlwaysBottomAppBarSpacedBetween() {
val context = LocalContext.current
val isTouchExplorationEnabled = remember {
val am = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
am.isEnabled && am.isTouchExplorationEnabled
}
val scrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior()
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
bottomBar = {
FlexibleBottomAppBar(
horizontalArrangement = Arrangement.SpaceBetween,
scrollBehavior = if (!isTouchExplorationEnabled) scrollBehavior else null,
content = {
IconButton(onClick = { /* doSomething() */ }) {
Icon(
Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Localized description"
)
}
IconButton(onClick = { /* doSomething() */ }) {
Icon(
Icons.AutoMirrored.Filled.ArrowForward,
contentDescription = "Localized description"
)
}
FilledIconButton(
modifier = Modifier.width(56.dp),
onClick = { /* doSomething() */ }
) {
Icon(Icons.Filled.Add, contentDescription = "Localized description")
}
IconButton(onClick = { /* doSomething() */ }) {
Icon(Icons.Filled.Check, contentDescription = "Localized description")
}
IconButton(onClick = { /* doSomething() */ }) {
Icon(Icons.Filled.Edit, contentDescription = "Localized description")
}
}
)
},
content = { innerPadding ->
LazyColumn(
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)
)
}
}
}
)
}
ExitAlwaysBottomAppBarSpacedEvenly
/**
* A sample for a [FlexibleBottomAppBar] that collapses when the content is scrolled up, and appears
* when the content scrolled down. The content is spaced evenly.
*/
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Preview
@Composable
fun ExitAlwaysBottomAppBarSpacedEvenly() {
val context = LocalContext.current
val isTouchExplorationEnabled = remember {
val am = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
am.isEnabled && am.isTouchExplorationEnabled
}
val scrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior()
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
bottomBar = {
FlexibleBottomAppBar(
horizontalArrangement = Arrangement.SpaceEvenly,
contentPadding = PaddingValues(horizontal = 0.dp),
scrollBehavior = if (!isTouchExplorationEnabled) scrollBehavior else null,
content = {
IconButton(onClick = { /* doSomething() */ }) {
Icon(
Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Localized description"
)
}
IconButton(onClick = { /* doSomething() */ }) {
Icon(
Icons.AutoMirrored.Filled.ArrowForward,
contentDescription = "Localized description"
)
}
FilledIconButton(
modifier = Modifier.width(56.dp),
onClick = { /* doSomething() */ }
) {
Icon(Icons.Filled.Add, contentDescription = "Localized description")
}
IconButton(onClick = { /* doSomething() */ }) {
Icon(Icons.Filled.Check, contentDescription = "Localized description")
}
IconButton(onClick = { /* doSomething() */ }) {
Icon(Icons.Filled.Edit, contentDescription = "Localized description")
}
}
)
},
content = { innerPadding ->
LazyColumn(
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)
)
}
}
}
)
}
ExitAlwaysBottomAppBarFixed
/**
* A sample for a [FlexibleBottomAppBar] that collapses when the content is scrolled up, and appears
* when the content scrolled down. The content arrangement is fixed.
*/
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Preview
@Composable
fun ExitAlwaysBottomAppBarFixed() {
val context = LocalContext.current
val isTouchExplorationEnabled = remember {
val am = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
am.isEnabled && am.isTouchExplorationEnabled
}
val scrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior()
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
bottomBar = {
FlexibleBottomAppBar(
horizontalArrangement = BottomAppBarDefaults.FlexibleFixedHorizontalArrangement,
scrollBehavior = if (!isTouchExplorationEnabled) scrollBehavior else null,
content = {
IconButton(onClick = { /* doSomething() */ }) {
Icon(
Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Localized description"
)
}
IconButton(onClick = { /* doSomething() */ }) {
Icon(
Icons.AutoMirrored.Filled.ArrowForward,
contentDescription = "Localized description"
)
}
FilledIconButton(
modifier = Modifier.width(56.dp),
onClick = { /* doSomething() */ }
) {
Icon(Icons.Filled.Add, contentDescription = "Localized description")
}
IconButton(onClick = { /* doSomething() */ }) {
Icon(Icons.Filled.Check, contentDescription = "Localized description")
}
IconButton(onClick = { /* doSomething() */ }) {
Icon(Icons.Filled.Edit, contentDescription = "Localized description")
}
}
)
},
content = { innerPadding ->
LazyColumn(
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)
)
}
}
}
)
}
ExitAlwaysBottomAppBarFixedVibrant
/**
* A sample for a vibrant [FlexibleBottomAppBar] that collapses when the content is scrolled up, and
* appears when the content scrolled down. The content arrangement is fixed.
*/
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Preview
@Composable
fun ExitAlwaysBottomAppBarFixedVibrant() {
val context = LocalContext.current
val isTouchExplorationEnabled = remember {
val am = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
am.isEnabled && am.isTouchExplorationEnabled
}
val scrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior()
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
bottomBar = {
FlexibleBottomAppBar(
horizontalArrangement = BottomAppBarDefaults.FlexibleFixedHorizontalArrangement,
scrollBehavior = if (!isTouchExplorationEnabled) scrollBehavior else null,
containerColor =
MaterialTheme.colorScheme.primaryContainer, // TODO(b/356885344): tokens
content = {
IconButton(onClick = { /* doSomething() */ }) {
Icon(
Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Localized description"
)
}
IconButton(onClick = { /* doSomething() */ }) {
Icon(
Icons.AutoMirrored.Filled.ArrowForward,
contentDescription = "Localized description"
)
}
FilledIconButton(
modifier = Modifier.width(56.dp),
onClick = { /* doSomething() */ }
) {
Icon(Icons.Filled.Add, contentDescription = "Localized description")
}
IconButton(onClick = { /* doSomething() */ }) {
Icon(Icons.Filled.Check, contentDescription = "Localized description")
}
IconButton(onClick = { /* doSomething() */ }) {
Icon(Icons.Filled.Edit, contentDescription = "Localized description")
}
}
)
},
content = { innerPadding ->
LazyColumn(
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)
)
}
}
}
)
}