0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Material 3 Expressive実装例 : Toolbar

Last updated at Posted at 2025-06-11

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(...)
        }
    }
)

また、horizontalArrangementBottomAppBarDefaults.FlexibleFixedHorizontalArrangement / BottomAppBarDefaults.FlexibleHorizontalArrangement に切り替えることで大画面でのアイテムの並び方が変わります。

FlexibleFixedHorizontalArrangement FlexibleHorizontalArrangement
Screenshot_20250610_180804.png Screenshot_20250610_180834.png

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
Screenshot_20250610_182046.png Screenshot_20250610_182714.png

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(...)
            }
            ...
        }
    )
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?