Material 3 Expressive の実装例の紹介シリーズです。
今回は大きく変更された Toolbar を紹介します。
(androidx.compose.material3:material3:1.5.0-alpha01 時点での内容になります。)
Material 3 Expressive での変更点
- Docked toolbar と Floating toolbar の 2 種類
- Material 3 Expressive では Bottom app bar よりも Docked toolbar を使うことが推奨される
- スタイルが Standard と Vibrant の 2 パターン
- Floating toolbar には Horizontal と Vertical の向きのパターンがある
実装
Docked toolbar
Docked toolbar という名前ではなく現時点では FlexibleBottomAppBar を使った実装になります。
FlexibleBottomAppBar(
horizontalArrangement = BottomAppBarDefaults.FlexibleFixedHorizontalArrangement,
content = {
IconButton(onClick = { }) {
Icon(...)
}
IconButton(onClick = { }) {
Icon(...)
}
FilledIconButton(
onClick = { },
modifier = Modifier.size(IconButtonDefaults.smallContainerSize()),
shape = IconButtonDefaults.smallSquareShape
) {
Icon(
painterResource(R.drawable.ic_add),
contentDescription = "Add",
modifier = Modifier.size(IconButtonDefaults.smallIconSize)
)
}
IconButton(onClick = { }) {
Icon(...)
}
IconButton(onClick = { /* doSomething() */ }) {
Icon(...)
}
}
)
また、horizontalArrangement を BottomAppBarDefaults.FlexibleFixedHorizontalArrangement / BottomAppBarDefaults.FlexibleHorizontalArrangement に切り替えることで大画面でのアイテムの並び方が変わります。
| FlexibleFixedHorizontalArrangement | FlexibleHorizontalArrangement |
|---|---|
![]() |
![]() |
Floating toolbar
Floating toolbar の実装には HorizontalFloatingToolbar を使います。VerticalFloatingToolbar に置き換えるだけで Vertical toolbar に切り替えが可能です。
// Horizontal
HorizontalFloatingToolbar(
modifier = Modifier
.align(Alignment.BottomCenter)
.offset(y = -FloatingToolbarDefaults.ScreenOffset),
expanded = expanded,
leadingContent = {
IconButton(onClick = { }) {
Icon(...)
}
IconButton(onClick = { }) {
Icon(...)
}
},
trailingContent = {
IconButton(onClick = { }) {
Icon(...)
}
IconButton(onClick = { }) {
Icon(...)
}
},
content = {
FilledIconButton(
onClick = { },
modifier = Modifier.size(
IconButtonDefaults.smallContainerSize(
IconButtonDefaults.IconButtonWidthOption.Wide
)
),
) {
Icon(
painterResource(R.drawable.ic_add),
contentDescription = "Add",
modifier = Modifier.size(IconButtonDefaults.smallIconSize)
)
}
}
)
// Vertical
VerticalFloatingToolbar(
modifier = Modifier
.align(Alignment.CenterEnd)
.offset(x = -FloatingToolbarDefaults.ScreenOffset),
expanded = expanded,
content = {
IconButton(onClick = { }) {
Icon(...)
}
IconButton(onClick = { }) {
Icon(...)
}
...
}
)
| Horizontal | Vertical |
|---|---|
![]() |
![]() |
Vibrant のスタイル
Vibrant のスタイルは FloatingToolbarDefaults.vibrantFloatingToolbarColors() が標準で用意されています。
HorizontalFloatingToolbar(
modifier = Modifier
.align(Alignment.BottomCenter)
.offset(y = -FloatingToolbarDefaults.ScreenOffset),
colors = FloatingToolbarDefaults.vibrantFloatingToolbarColors(),
leadingContent = {
...
},
trailingContent = {
...
},
content = {
...
}
)
スクロールとの連動
Modifier#floatingToolbarVerticalNestedScroll を使うことでスクロールに合わせて Floating toolbar を折りたたむことができます。
var expanded by rememberSaveable { mutableStateOf(true) }
Box(
modifier = Modifier.floatingToolbarVerticalNestedScroll(
expanded = expanded,
onExpand = { expanded = true },
onCollapse = { expanded = false },
),
) {
LazyColumn { ... }
HorizontalFloatingToolbar(
modifier = Modifier
.align(Alignment.BottomCenter)
.offset(y = -FloatingToolbarDefaults.ScreenOffset),
expanded = expanded,
leadingContent = {
IconButton(onClick = { }) {
Icon(...)
}
...
},
trailingContent = {
IconButton(onClick = { }) {
Icon(...)
}
...
},
content = {
FilledIconButton(
onClick = { },
modifier = Modifier.size(
IconButtonDefaults.smallContainerSize(
IconButtonDefaults.IconButtonWidthOption.Wide
)
),
) {
Icon(
painterResource(R.drawable.ic_add),
contentDescription = "Add",
modifier = Modifier.size(IconButtonDefaults.smallIconSize)
)
}
}
)
}
別パターンで FloatingToolbarDefaults.exitAlwaysScrollBehavior を使うことでスクロールに連動して Floating toolbar を画面外へアニメーションさせることも可能となっています。
val exitAlwaysScrollBehavior = FloatingToolbarDefaults.exitAlwaysScrollBehavior(exitDirection = Bottom)
Box(
modifier = Modifier.nestedScroll(exitAlwaysScrollBehavior),
) {
LazyColumn { ... }
HorizontalFloatingToolbar(
modifier = Modifier
.align(Alignment.BottomCenter)
.offset(y = -FloatingToolbarDefaults.ScreenOffset),
expanded = true,
scrollBehavior = exitAlwaysScrollBehavior,
leadingContent = {
IconButton(onClick = { }) {
Icon(...)
}
...
},
trailingContent = {
IconButton(onClick = { }) {
Icon(...)
}
...
},
content = {
FilledIconButton(
onClick = { },
modifier = Modifier.size(
IconButtonDefaults.smallContainerSize(
IconButtonDefaults.IconButtonWidthOption.Wide
)
),
) {
Icon(
painterResource(R.drawable.ic_add),
contentDescription = "Add",
modifier = Modifier.size(IconButtonDefaults.smallIconSize)
)
}
}
)
}
FAB との組み合わせ
Floating toolbar には FAB との連携が標準で用意されており、Standard と Vibrant それぞれのスタイルがあります。
Standard には FloatingToolbarDefaults.StandardFloatingActionButton を、Vibrant には FloatingToolbarDefaults.VibrantFloatingActionButton を使用します。
// Standard
HorizontalFloatingToolbar(
expanded = true,
floatingActionButton = {
FloatingToolbarDefaults.StandardFloatingActionButton(
onClick = { },
) {
Icon(
painterResource(R.drawable.ic_add),
contentDescription = "Add"
)
}
},
content = {
IconButton(onClick = { }) {
Icon(...)
}
...
}
)
// Vibrant
HorizontalFloatingToolbar(
expanded = true,
colors = FloatingToolbarDefaults.vibrantFloatingToolbarColors(),
floatingActionButton = {
FloatingToolbarDefaults.VibrantFloatingActionButton(
onClick = { },
) {
Icon(
painterResource(R.drawable.ic_add),
contentDescription = "Add"
)
}
},
content = {
IconButton(onClick = { }) {
Icon(...)
}
...
}
)
FAB との組み合わせにおいてもコンテンツのスクロールと連携する機能が標準で用意されています。
先ほどの FloatingToolbarDefaults.exitAlwaysScrollBehavior は FAB がある状態でも機能します。
val exitAlwaysScrollBehavior = FloatingToolbarDefaults.exitAlwaysScrollBehavior(exitDirection = Bottom)
Box(
modifier = Modifier.nestedScroll(exitAlwaysScrollBehavior),
) {
LazyColumn { ... }
HorizontalFloatingToolbar(
modifier = Modifier
.align(Alignment.BottomCenter)
.offset(y = -FloatingToolbarDefaults.ScreenOffset),
expanded = true,
scrollBehavior = exitAlwaysScrollBehavior,
floatingActionButton = {
FloatingToolbarDefaults.StandardFloatingActionButton(
onClick = { },
) {
Icon(
painterResource(R.drawable.ic_add),
contentDescription = "Add"
)
}
},
content = {
IconButton(onClick = { }) {
Icon(...)
}
...
}
)
}
スクロールに合わせて FAB に Floating toolbar が折りたたまれるという別パターンの場合は floatingToolbarVerticalNestedScroll を使用して次のような実装になります。
var expanded by rememberSaveable { mutableStateOf(true) }
Box(
modifier = Modifier.floatingToolbarVerticalNestedScroll(
expanded = expanded,
onExpand = { expanded = true },
onCollapse = { expanded = false },
),
) {
LazyColumn { ... }
HorizontalFloatingToolbar(
modifier = Modifier
.align(Alignment.BottomEnd)
.offset(x = -FloatingToolbarDefaults.ScreenOffset, y = -FloatingToolbarDefaults.ScreenOffset),
expanded = expanded,
floatingActionButton = {
FloatingToolbarDefaults.StandardFloatingActionButton(
onClick = { },
) {
Icon(
painterResource(R.drawable.ic_add),
contentDescription = "Add"
)
}
},
content = {
IconButton(onClick = { }) {
Icon(...)
}
...
}
)
}



