Top Sheet Dialogを実現する

今回はJetpack composeでTop Sheet Dialogを実現するプログラムに挑戦してみました! Material DesignのBottom sheets という部品を利用すれば、下部からダイアログを表示することはできますが、 上部からの表示はサポートされていないため、カスタムレイアウトやアニメーションを使用して、 BottomSheetのような動作を上部から表示させるための工夫をする必要があります。 これには、AnimatedVisibilityやSlideなどのJetpack ComposeのアニメーションとトランジションAPIを使用します。 今回の開発環境はAndroid Studio Giraffe(2022.3.1)を利用しています。

※この記事は2024/05/12時点の情報です。

build.gradle(Module:app)
依存関係には現時点で最新のバージョンを指定しています。

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
}

android {
    namespace 'com.example.topsheet'
    compileSdk 33

    defaultConfig {
        applicationId "com.example.topsheet"
        minSdk 29
        targetSdk 33
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        vectorDrawables {
            useSupportLibrary true
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
    buildFeatures {
        compose true
    }
    composeOptions {
        kotlinCompilerExtensionVersion '1.4.3'
    }
    packaging {
        resources {
            excludes += '/META-INF/{AL2.0,LGPL2.1}'
        }
    }
}

dependencies {
    // Jetpack Composeのバージョン
    def compose_version = "1.0.5" // このバージョンは適宜更新してください
    // Material3のバージョン
    def material3_version = "1.1.0-alpha06" // このバージョンは適宜更新してください

    implementation "androidx.compose.material3:material3:$material3_version"
    implementation "androidx.compose.ui:ui:$compose_version"
    implementation "androidx.compose.material:material:$compose_version"
    implementation "androidx.compose.ui:ui-tooling:$compose_version"
    implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
    implementation "androidx.compose.runtime:runtime-rxjava2:$compose_version"
    implementation "androidx.lifecycle:lifecycle-viewmodel-compose:1.0.0-alpha07"
    implementation "androidx.activity:activity-compose:1.3.1"
}

MainActivity.kt
このコードでは、AnimatedVisibilityとSlideを使用して、上部からスライドダウンするパネルを作成しています。 アプリバーのメニューボタンをクリックするとTopSheetが表示され、TopSheet内のボタンをクリックするとTopSheetが非表示になります。

package com.example.topsheet

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.foundation.layout.*
import androidx.compose.material.Button
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.Scaffold
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Menu
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.example.topsheet.ui.theme.TopsheetTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            TopsheetTheme {
                Surface(
                    modifier = Modifier.fillMaxSize()
                ) {
                    TopSheetExample()
                }
            }
        }
    }
}

@Composable
fun TopSheetExample() {
    var showTopSheet by remember { mutableStateOf(false) }

    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text("Top Sheet Example") },
                actions = {
                    IconButton(onClick = { showTopSheet = true }) {
                        Icon(Icons.Filled.Menu, contentDescription = "Menu")
                    }
                }
            )
        }
    ) { contentPadding ->
        Box(modifier = Modifier.fillMaxSize()) {
            // メインコンテンツはココに記述

            // TopSheetコンテンツ
            AnimatedVisibility(
                visible = showTopSheet,
                enter = slideInVertically(initialOffsetY = { -it }),
                exit = slideOutVertically(targetOffsetY = { -it })
            ) {
                Surface(
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(200.dp),
                    color = Color.LightGray,
                    elevation = 10.dp
                ) {
                    Column(
                        modifier = Modifier
                            .fillMaxSize()
                            .padding(16.dp),
                        verticalArrangement = Arrangement.Center,
                        horizontalAlignment = Alignment.CenterHorizontally
                    ) {
                        Text(text = "This is a top sheet")
                        Spacer(modifier = Modifier.height(8.dp))
                        Button(onClick = { showTopSheet = false }) {
                            Text("Hide")
                        }
                    }
                }
            }
        }
    }
}

処理結果は次の通りです。メニューをクリックすると上部からTopSheetが表示されます。

Jetpack composeでTop Sheetを実現

こういった部品はありそうで無かったりするので、 同じような機能を実現するために、どのコンポーネントを組み合わせるかイメージができるとカスタム部品を作りやすいかも知れませんね。 イメージできそうですか?

etpack composeのアニメーションを覚えられましたか?

あまりイメージできなかった人は、どんな部品でも良いので、自分なりのカスタム部品を作ってみてはいかがでしょうか?

管理人情報