티스토리 뷰

안드로이드/코드

안드로이드 Compose BottomSheet

알렌보이스 2022. 11. 6. 20:47
728x90

Compose에서 BottomSheet를 사용하는 방법은 2가지가 있습니다.

BottomSheetScaffold와 ModalBottomSheetLayout 두 가지 방식인데 둘의 차이점은 아래에서 다루도록 하겠습니다.

1. BottomSheetScaffold

Group 478665.png

val state = rememberBottomSheetScaffoldState(
    bottomSheetState = BottomSheetState(BottomSheetValue.Collapsed)
)
val scope = rememberCoroutineScope()

BottomSheetScaffold(
    scaffoldState = state,
    sheetContent = {
        BottomSheetContent(state)
    },
    sheetPeekHeight = 40.dp,
    modifier = Modifier.fillMaxSize()
) {
    MainContent {
        scope.launch {
            if (state.bottomSheetState.isCollapsed) {
                state.bottomSheetState.expand()
            } else {
                state.bottomSheetState.collapse()
            }
        }
    }
}

BottomSheetScaffold는 이름에서 알 수 있듯이 Scaffold의 확장형 버전입니다.

BottomSheet 기능 외에도 Topbar, Drawer를 추가할 수 있습니다.

사용 방법 역시 기본 Scaffold와 거의 동일합니다.

@Composable
fun MainContent(
    onClickListener: () -> Unit
) {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(vertical = 20.dp, horizontal = 24.dp)
    ) {
        Text(
            text = "Bottom Sheet",
            style = Typography.h4,
            fontWeight = FontWeight.Bold
        )

        Button(onClick = onClickListener) {
            Text(text = "버튼", style = Typography.body1)
        }
    }
}

content 블록은 메인으로 보이는 UI를 넣어주면 됩니다.

이번 예시에서는 Text와 Button으로 단순하게 표시하였습니다.

@OptIn(ExperimentalMaterialApi::class)
@Composable
fun BottomSheetContent(state: BottomSheetScaffoldState) {
    Card(
        shape = RoundedCornerShape(topStart = 20.dp, topEnd = 20.dp),
        border = BorderStroke(1.dp, Color.Black),
        modifier = Modifier
            .fillMaxWidth()
    ) {
        Column(
            modifier = Modifier.padding(horizontal = 20.dp)
        ) {
            val rotate by animateFloatAsState(
                targetValue = if (state.bottomSheetState.isCollapsed) 0f else 180f
            )

            Spacer(modifier = Modifier.height(8.dp))
            Icon(
                painter = painterResource(id = R.drawable.ic_arrow_up),
                contentDescription = "",
                tint = Color.Black,
                modifier = Modifier
                    .rotate(rotate)
                    .align(Alignment.CenterHorizontally)
            )

            Spacer(modifier = Modifier.height(15.dp))
            Text(text = "Bottom Sheet Scaffold", fontWeight = FontWeight.Bold)

            Spacer(modifier = Modifier.height(15.dp))
            Text(text = "Is Open")

            Spacer(modifier = Modifier.height(30.dp))
        }
    }
}

sheetContent 영역에는 원하는 BottomSheet의 UI를 넣어주면 됩니다.

BottomSheet는 BottomSheetState를 통해 상태가 관리되며

val state = rememberBottomSheetScaffoldState(
    bottomSheetState = BottomSheetState(BottomSheetValue.Collapsed)
)

rememberBottomSheetScaffoldState를 통해 변수를 생성할 수 있습니다.

초깃값으로 Collapsed와 Expanded를 사용할 수 있습니다.

scope.launch {
  if (state.bottomSheetState.isCollapsed) {
      state.bottomSheetState.expand()
  } else {
      state.bottomSheetState.collapse()
  }
}

클릭 등의 이베트를 통해 BottomSheet의 state의 상태 값을 변경하기 위해서는

expand() 혹은 collapse()를 이용하면 됩니다.

두 함수는 suspend 함수이므로 코루틴이 필요합니다.

BottomSheetScaffold에서 BottomSheet를 관리하는 속성은 다음과 같습니다.

  • sheetGesturesEnabled : BottomSheet를 제스처를 통한 상호작용 작동 여부
  • sheetShape : BottomSheet의 모양 설정
  • sheetElevation : BottomSheet의 그림자, 기본 값 8Dp
  • sheetBackgroundColor : BottomSheet의 배경 색
  • sheetContentColor : BottomSheet의 컨텐츠 색상
  • sheetPeekHeight : BottomSheet를 접은 상태의 크기, 기본값 56Dp

2. ModalBottomSheetLayout

Group 478664.png

val modalState = rememberModalBottomSheetState(
    initialValue = ModalBottomSheetValue.Hidden,
    skipHalfExpanded = false
)
val scope = rememberCoroutineScope()

ModalBottomSheetLayout(
    sheetState = modalState,
    sheetContent = {
        ModalBottomSheetContents()
    }
) {
    Column(
        modifier = Modifier
            .fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        ModalBottomSheetMain {
            scope.launch {
                modalState.show()
            }
        }
    }
}

@Composable
fun ModalBottomSheetMain(
    onClickListener: () -> Unit
) {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(vertical = 20.dp, horizontal = 24.dp)
    ) {
        Text(
            text = "Modal Bottom Sheet",
            style = Typography.h4,
            fontWeight = FontWeight.Bold
        )

        Button(onClick = onClickListener) {
            Text(text = "버튼", style = Typography.body1)
        }
    }
}

@OptIn(ExperimentalMaterialApi::class)
@Composable
fun ModalBottomSheetContents() {
    LazyColumn {
        items(50) {
            ListItem(
                text = { Text("Item $it") },
                icon = {
                    Icon(
                        Icons.Default.Favorite,
                        contentDescription = "Localized description"
                    )
                }
            )
        }
    }
}

ModalBottomSheetLayout은 BottomSheetScaffold와 동일하게 content 영역과 sheetContent 영역을 구분을 두어서 UI를 구현하면 됩니다.

ModalBottomSheetState를 통해 상태가 관리되며 rememberModalBottomSheetState를 통해 변수를 생성할 수 있습니다.

ModalBottomSheetLayout은 3가지 상태 값을 가집니다.

  • Hidden : 숨김 상태
  • Expanded : 전체 펼쳐진 상태
  • HalfExpanded : BottomSheet의 높이가 디바이스의 높이의 50%까지만 펼쳐진 상태

skipHalfExpanded를 True로 설정하면 HalfExpanded가 없이 2가지 상태로 관리할 수 있습니다.

이 외에도 animationSpec, confirmStateChange를 설정할 수 있습니다.

BottomSheet를 펼치기/접기 위해서는 위에서 만든 state의 show()/hide()를 이용하면 됩니다.
역시 suspend 함수이므로 코루틴이 필요합니다.

이 외에 ModalBottomSheetLayout에서 설정할 수 있는 속성들은 다음과 같습니다.

  • sheetShape : BottomSheet의 모양
  • sheetElevation : BottomSheet의 그림자, 기본 값 16Dp
  • sheetBackgroundColor : BottomSheet의 배경 색상
  • sheetContentColor : BottomSheet의 컨텐츠 색상
  • scrimColor : BottomSheet Show 상태에서 BottomSheet 영역 외의 색상

BottomSheetScaffold와 ModalBottomSheetLayout의 가장 큰 차이점은 다이얼로그처럼 딤 효과의 유무와 접혀 있을 때 화면에 표시 가능 여부입니다.

두 개 다 사용해 보고 상황에 맞게 선택해서 사용하시기 바랍니다.

728x90

'안드로이드 > 코드' 카테고리의 다른 글

Compose 내 관리앱 만들기 : 1. 프로젝트 구성  (0) 2023.03.19
코드 분석  (0) 2023.01.24
Compose QR코드 스캐너  (2) 2022.11.04
Android Compose MotionLayout 2  (0) 2022.11.03
Compose MotionLayout  (0) 2022.11.02
댓글