Material 3 Expressive の実装例の紹介シリーズです。
今回は大きく変更された Toolbar を紹介します。
(androidx.compose.material3:material3:1.4.0-alpha15
時点での内容になります。)
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(...)
}
...
}
)
}
