Jetpack ComposeでTopAppBarとStatus Barの色をスクロールに合わせて変化させたい

この記事をシェア
JetpackCompose一問一答

Material3のTopAppBarは、コンテンツのスクロールに合わせて色が変化します。今回はこれの実現方法を紹介します。さらに、StatusBarの色もTopAppBarに合わせて変化させる方法も説明します。

やりたいこと

Material3のTopAppBar(画面のタイトルなどが表示される部分。従来はToolBarやActionBarとも呼ばれていた。)は、コンテンツのスクロールに合わせて色を変化させるとガイドラインに定められています。一方でStatusBar(通知や時計などが表示される部分)の色はガイドラインには明記されていませんが、GmailなどのGoogle製アプリやAndroid標準の設定アプリなどでは、TopAppBarの色に合わせてStatusBarの色も変化しています。今回はこれを、TopAppBarScrollBehaviorとAccompanistのSystem UI Controllerを使って実現します。

環境

  • Kotlin 1.6.10
  • Jetpack Compose 1.1.1
  • Material3 1.0.0 alpha8
  • Accompanist 0.23.1

Jetpack Compose周辺は日々変化していますので、最新の情報を参照して下さい。

ソースコードと結果

依存関係の追加

dependencies {
    implementation 'androidx.compose.material3:material3:1.0.0-alpha08'
    implementation 'com.google.accompanist:accompanist-systemuicontroller:0.23.1'
}

コンポーザブル

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AppScreen() {
    val scrollBehavior = remember { TopAppBarDefaults.pinnedScrollBehavior() }
    val statusBarColor = TopAppBarDefaults.centerAlignedTopAppBarColors()
        .containerColor(scrollFraction = scrollBehavior.scrollFraction).value
    val systemUiController = rememberSystemUiController()

    SideEffect {
        systemUiController.setStatusBarColor(statusBarColor)
    }

    Scaffold(
        modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
        topBar = {
            CenterAlignedTopAppBar(
                title = { Text("TopAppBar") },
                scrollBehavior = scrollBehavior
            )
        }
    ) {
        LazyColumn {
            items(100) { count ->
                Text(
                    text = "Item ${count + 1}",
                    modifier = Modifier.fillMaxWidth().height(30.dp).padding(20.dp, 4.dp)
                )
            }
        }
    }
}

結果

説明

依存関係の追加

dependencies {
    implementation 'androidx.compose.material3:material3:1.0.0-alpha08'
    implementation 'com.google.accompanist:accompanist-systemuicontroller:0.23.1'
}

Material3に移行していることが前提になります。build.gradledependenciesには、material3ライブラリを追加します。Material3への移行について、詳しくは「Jetpack ComposeアプリをMaterial Design 3へ移行する」を参照してください。

また、AccompanistのSystem UI Controllerを使います。accompanist-systemuicontrollerを追加しておきます。System UI Controllerを使ってStatus Barの色を変化させる方法は、「Jetpack ComposeでStatus Barの色を変更したい」にも詳しく書いていますので、そちらも参考にしてください。

TopAppBarの色の変化

val scrollBehavior = remember { TopAppBarDefaults.pinnedScrollBehavior() }
Scaffold(
    modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
    topBar = {
        CenterAlignedTopAppBar(
            title = { Text("TopAppBar") },
            scrollBehavior = scrollBehavior
        )
    }
) {
    LazyColumn {
        ...
    }
}

スクロールに合わせてTopAppBarの色を変化させるには、androidx.compose.material3パッケージのTopAppBarScrollBehaviorを使います。

TopAppBarScrollBehaviorには、コンテンツをスクロールした時のTopAppBarの振る舞いが定義されていて、TopAppBarDefaultsに3種類(pinnedScrollBehavior, enterAlwaysScrollBehavior, exitUntilCollapsedScrollBehavior)が定義されています。最も基本的なpinnedScrollBehaviorは、TopAppBarの位置やサイズは固定で、色のみ変化させます。

scrollBehaviorrememberで定義しているのは、このあとStatus Barの色を設定するために必要になるからです。TopAppBarの色だけであれば、rememberではなく普通の変数として定義しても問題ないです。

TopAppBarとスクロールコンテンツ(上の例ではLazyColumn)は、Scaffoldを使ってレイアウトします。作成したscrollBehaviorオブジェクトをScaffold内のTopAppBarscrollBehaviorにセットし、Scaffold自体のmodifierscrollBehavior.nestedScrollConnectionを渡すことによって、コンテンツのスクロール状態とTopAppBarの状態が同期します。

StatusBarの色の変化

val scrollBehavior = remember { TopAppBarDefaults.pinnedScrollBehavior() }
val statusBarColor = TopAppBarDefaults.centerAlignedTopAppBarColors()
    .containerColor(scrollFraction = scrollBehavior.scrollFraction).value
val systemUiController = rememberSystemUiController()

SideEffect {
    systemUiController.setStatusBarColor(statusBarColor)
}

StatusBarの色は、AccompanistのSystem UI Controllerを使って設定します。先ほど作成したscrollBehaviorオブジェクトを使って、TopAppBarの現在の色を取得し、setStatusBarColor()に渡します。

TopAppBarの色は、TopAppBarDefaultsから取得します。この時、scrollFractionscrollBehaviorオブジェクトのscrollFractionを渡すことによって、現在のスクロール状態に合わせた色を取得できます。

JetpackCompose一問一答

Jetpack Compose一問一答

コンテンツは随時追加していきます。

この記事をシェア