nakashimaakioのブログ

Androidアプリエンジニア / 記事に「はてなスター」よろしくお願いします。

【Android Jetpack Compose】下スワイプで画面を閉じる

はじめに

Android Jetpack Composeで、下スワイプで画面を閉じる方法を解説。ModalBottomSheetを用いる方法もあるが、この記事ではAnchoredDraggableStateによりスワイプ検知して画面を閉じる方法について解説。

方法

1. スワイプ開始・終了クラスの作成

スワイプ状態を定義するクラス作成。

enum class AnchorPos {
    Start, End;
}

2. モーダル画面の作成

モーダル画面を作成

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun ModalScreen(navController: NavHostController) {
    val screenHeight = with(LocalDensity.current) { LocalConfiguration.current.screenHeightDp.dp.toPx() }
    val anchors = DraggableAnchors {
        AnchorPos.Start at 0f
        AnchorPos.End at screenHeight
    }

    // ドラッグ可能な距離と状態を設定
    val draggableState = remember {
        AnchoredDraggableState(
            initialValue = AnchorPos.Start,
            anchors = anchors,
            positionalThreshold = { it * 0.5f },
            velocityThreshold = { screenHeight * 0.5f },
            snapAnimationSpec = SpringSpec(),
            decayAnimationSpec = exponentialDecay(),
            confirmValueChange = {
                when (it) {
                    AnchorPos.Start -> {}
                    AnchorPos.End -> {
                        //複数回popが行われることを防ぐため、現在の画面でのみpopを行う。
                        if (navController.currentDestination?.route == NavItem.MODAL.route) {
                            navController.popBackStack()
                        }
                    }
                }
                true
            }
        )
    }

    Box(
        modifier = Modifier
            .offset {
                IntOffset(
                    x = 0,
                    y = draggableState
                        .requireOffset()
                        .roundToInt()
                )
            }
            .anchoredDraggable(
                state = draggableState,
                orientation = Orientation.Vertical
            )
            .fillMaxSize()
            .background(Color.DarkGray),
    ) {
        //戻るボタン
        Button(
            onClick = { navController.popBackStack() },
            colors = ButtonDefaults.buttonColors(containerColor = Color.Transparent),
            contentPadding = PaddingValues(0.dp)
        ) {
            Icon(
                modifier = Modifier.size(32.dp),
                contentDescription = "Close",
                imageVector = Icons.Default.Close
            )
        }
    }
}

参考サイト

zenn.dev