[Kotlin / Compose] BottomSheet Migration

Subeen·2024년 5월 16일
0

Compose

목록 보기
14/20

BottomSheetDialog

BottomSheetDialog 함수는 Compose를 사용하여 바텀 시트 다이얼로그를 생성하는 역할을 하며, 바텀 시트 다이얼로그를 표시하여 사용자가 특정 항목을 제거하거나 취소할 수 있도록 한다.
sheetState를 통해 다이얼로그의 상태를 관리하고, onRemoveClickListenerdismissBottomSheet를 통해 사용자 인터랙션을 처리한다.

BottomSheetDialog의 주요 요소 및 역할

  • 매개변수
    • onRemoveClickListener: (SearchModel) -> Unit : 사용자가 항목 제거 버튼을 클릭했을 때 호출되는 콜백 함수로 제거할 항목을 인자로 받는다.
    • searchModel: SearchModel : 현재 선택 된 SearchModel 객체로 다이얼로그가 어떤 항목을 대상으로 동작하는지 알기 위해 사용된다.
    • dismissBottomSheet: () -> Unit : 다이얼로그를 닫기 위한 함수로 사용자가 다이얼로그 외부를 클릭하거나 취소 버튼을 클릭했을 때 호출된다.
  • 상태 관리
    • val sheetState = rememberModalBottomSheetState() : 바텀 시트의 상태를 관리하기 위한 상태 객체로 다이얼로그의 열림/닫힘 상태를 추적한다.
  • ModalBottomSheet
    • onDismissRequest = dismissBottomSheet : 사용자가 다이얼로그 외부를 클릭했을 때 다이얼로그를 닫는 함수이다.
    • sheetState = sheetState : 바텀 시트의 상태를 전달한다.
@Composable
fun GradientProvider(): List<Color> { // 색상 목록을 반환
    return listOf(
        colorResource(id = R.color.color_1), // 첫 번째 색상
        colorResource(id = R.color.color_2), // 두 번째 색상
        colorResource(id = R.color.color_3)  // 세 번째 색상
    )
}

// ExperimentalMaterial3Api를 옵트인하여 사용
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BottomSheetDialog(
    onRemoveClickListener: (SearchModel) -> Unit, // 항목 제거 버튼 클릭 시 호출되는 함수
    searchModel: SearchModel, // 현재 선택된 SearchModel 객체
    dismissBottomSheet: () -> Unit, // 바텀 시트를 닫는 함수
) {
    val sheetState = rememberModalBottomSheetState() // 바텀 시트의 상태를 기억하기 위한 변수

    ModalBottomSheet(
        onDismissRequest = dismissBottomSheet, // 바텀 시트 외부를 클릭했을 때 닫히도록 설정
        sheetState = sheetState, // 바텀 시트 상태
        shape = RoundedCornerShape(topStart = 20.dp, topEnd = 20.dp), // 바텀 시트의 모서리 모양
        contentColor = Color.White, // 내용물 색상
        dragHandle = null, // 드래그 핸들 비활성화
        modifier = Modifier.fillMaxWidth() // 바텀 시트의 너비를 화면 가득 채우기
    ) {
        Column(
            modifier = Modifier
                .background(color = Color.White), // 배경색 설정
            verticalArrangement = Arrangement.Center, // 수직 중앙 정렬
            horizontalAlignment = Alignment.CenterHorizontally // 수평 중앙 정렬
        ) {
            // 제목 텍스트
            Text(
                text = stringResource(id = R.string.storage_bottom_sheet_title),
                fontWeight = FontWeight.Bold,
                fontSize = 16.sp,
                textAlign = TextAlign.Center,
                modifier = Modifier
                    .padding(top = 12.dp, bottom = 12.dp)
            )
            // 메시지 텍스트
            Text(
                text = stringResource(id = R.string.storage_bottom_sheet_message),
                fontSize = 14.sp,
                textAlign = TextAlign.Center,
                modifier = Modifier.padding(bottom = 12.dp)
            )
            Row(
                modifier = Modifier.fillMaxWidth(), // 너비를 화면 가득 채우기
                horizontalArrangement = Arrangement.Center
            ) {
                Button(
                    onClick = {
                        dismissBottomSheet() // 취소 버튼 클릭 시 바텀 시트 닫기
                    },
                    modifier = Modifier
                        .weight(1f) // 버튼 너비를 동일하게 설정
                        .padding(start = 12.dp, end = 4.dp), // 패딩 설정
                    colors = ButtonDefaults.buttonColors(backgroundColor = colorResource(id = R.color.color_1)), // 버튼 배경색 설정
                    shape = RoundedCornerShape(32.dp), // 버튼 모서리 모양 설정
                    border = BorderStroke(
                        width = 1.dp, // 테두리 두께
                        color = colorResource(id = R.color.color_1) // 테두리 색상
                    )
                ) {
                    Text(
                        text = stringResource(id = R.string.bt_cancel), // 취소 버튼 텍스트
                        color = Color.White // 텍스트 색상
                    )
                }
                Spacer(modifier = Modifier.width(16.dp)) // 버튼 간 간격
                Button(
                    onClick = {
                        onRemoveClickListener.invoke(searchModel) // 제거 버튼 클릭 시 호출
                        dismissBottomSheet() // 바텀 시트 닫기
                    },
                    modifier = Modifier
                        .weight(1f) // 버튼 너비를 동일하게 설정
                        .padding(start = 4.dp, end = 12.dp, bottom = 12.dp), // 패딩 설정
                    colors = ButtonDefaults.buttonColors(backgroundColor = Color.White), // 버튼 배경색 설정
                    shape = RoundedCornerShape(32.dp), // 버튼 모서리 모양 설정
                    border = BorderStroke(
                        width = 1.dp, // 테두리 두께
                        brush = Brush.linearGradient( // 선형 그라디언트 브러쉬 설정
                            colors = GradientProvider(), // 그라디언트 색상 목록
                            start = Offset.Zero, // 시작 오프셋
                            end = Offset.Infinite // 종료 오프셋
                        )
                    )
                ) {
                    Text(
                        text = stringResource(id = R.string.bt_remove), // 제거 버튼 텍스트
                        color = colorResource(id = R.color.color_1)
                    )
                }
            }
        }
    }
}

StorageScreen

StorageScreen 함수는 Compose를 사용하여 저장된 항목 목록을 표시하고 사용자가 특정 항목을 클릭했을 때 바텀 시트 다이얼로그를 표시하는 역할을 한다.

StorageScreen의 주요 요소 및 역할

  • ViewModel
    • val searchState by viewModel.storageItems.observeAsState() : StorageViewModelstorageItems을 관찰하여 저장된 항목 목록의 상태를 searchState로 가져온다.
    • var bottomSheetState by remember { mutableStateOf(BottomSheetState()) } : 바텀 시트의 상태를 관리하기 위한 bottomSheetState 변수를 초기화한다. 바텀 시트의 열림 여부와 선택된 항목을 저장한다.
  • LaunchedEffect
    • LaunchedEffect(viewModel) : viewModel이 초기화될 때 호출되어 viewModel.getStorageItems()를 통해 저장된 항목을 가져온다.
  • SearchList 컴포저블
    • 저장된 항목을 표시하며 searchState를 통해 항목 목록을 가져오고, 사용자가 항목을 클릭했을 때 bottomSheetState를 업데이트하여 바텀 시트를 연다.
// BottomSheetState 데이터 클래스는 바텀 시트의 상태를 관리
data class BottomSheetState(
    val isOpen: Boolean = false, // 바텀 시트 열림 여부
    val searchModel: SearchModel? = null // 선택된 SearchModel 객체
)

@Composable
fun StorageScreen(viewModel: StorageViewModel = hiltViewModel()) {
    val searchState by viewModel.storageItems.observeAsState() // 저장된 항목 상태 관찰
    var bottomSheetState by remember { mutableStateOf(BottomSheetState()) } // 바텀 시트 상태 기억

    // viewModel이 초기화될 때 호출
    LaunchedEffect(viewModel) {
        viewModel.getStorageItems() // 저장된 항목을 가져오는 함수 호출
    }

    Column(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.White)
    ) {
        SearchList(
            items = searchState ?: emptyList(), // 검색 항목 목록
            onItemClick = { searchModel ->
                bottomSheetState = BottomSheetState(isOpen = true, searchModel = searchModel) // 항목 클릭 시 바텀 시트 열기
            },
            onScrollEnd = { viewModel.getStorageItems() } // 스크롤 끝에 도달 시 추가 항목 가져오기
        )
    }

    if (bottomSheetState.isOpen) { // 바텀 시트가 열려 있을 때
        val currentSearchModel = bottomSheetState.searchModel ?: error("searchModel is null") // 현재 선택된 SearchModel 객체
        BottomSheetDialog(
            onRemoveClickListener = {
                viewModel.removeStorageItem(currentSearchModel) // 항목 제거 함수 호출
                bottomSheetState = BottomSheetState() // 바텀 시트 상태 초기화
            },
            searchModel = currentSearchModel, // 현재 선택된 SearchModel 객체 전달
            dismissBottomSheet = { bottomSheetState = BottomSheetState() } // 바텀 시트 닫는 함수
        )
    }
}
profile
개발 공부 기록 🌱

0개의 댓글