1. 기본 공통 알림 팝업

공통으로 사용할 팝업은 크게 가로 버튼 1개, 가로 버튼 2개, 세로 버튼 2개 UI로 구성 되어있다 따라서 DialogType을 정의 해준뒤 필요한 요소를 넣을수있게 만들었다!

스크린샷 2024-05-22 오전 9.22.01.png

@ExperimentalComposeUiApi
@Composable
fun CommonDialog(
    dialogType: Int,
    modifier: Modifier = Modifier,
    onDismissRequest: () -> Unit = {},
    imageDrawable: Int? = null,     //ex) imageDrawable = "R.id.img_welcomechall_stamp1"
    imageRaw: String? = null,
    title: String = "",
    msg: String = "",
    btn_positive: String = "",
    btn_negative: String = "",
    hasCloseButton: Boolean = false,
    isCancelable: Boolean = false,
    onClickCallback: (Int) -> Unit = {},
) {
    /**
     * 우리 프로젝트 환경에선 Dialog 쓰면 화면 위에 살짝 공백이 생겨서 Dialog 대신 Popup으로 변경
     */

    Popup(
        onDismissRequest = onDismissRequest,
        properties = PopupProperties(
            focusable = true,
            dismissOnBackPress = isCancelable,
            dismissOnClickOutside = isCancelable,
        ),
    ) {
//    Dialog(
//        onDismissRequest = onDismissRequest,
//        properties = DialogProperties(
//            dismissOnBackPress = isCancelable,
//            dismissOnClickOutside = isCancelable,
//        ),
//    ) {
        Box(
            contentAlignment = Alignment.Center,
            modifier = modifier
                .fillMaxSize()
                .background(Transparent70)
        ) {
            Card(
                modifier = modifier
                    .padding(end = 32.dp, start = 32.dp)
                    .wrapContentSize(),
                shape = RoundedCornerShape(16.dp),
                backgroundColor = Gray600,
                contentColor = Gray600,
            ) {
                Column(
                    modifier = modifier
                        .padding(top = 32.dp, end = ScreenSideMargin, start = ScreenSideMargin, bottom = 24.dp)
                        .fillMaxWidth(),
                    horizontalAlignment = Alignment.CenterHorizontally
                ) {
                    if (hasCloseButton) {
                        Box(modifier = modifier
                            .wrapContentSize()
                            .align(Alignment.End)
                            .padding(5.dp)
                            .clickable { onDismissRequest.invoke() }
                        ) {
                            Image(
                                painter = painterResource(id = R.drawable.ic_popup_close),
                                contentDescription = "",
                                modifier = modifier,
                            )
                        }
                    }

                    if (imageDrawable != null) {
                        Spacer(modifier = modifier.height(5.dp))
                        Image(
                            painter = painterResource(id = imageDrawable),
                            contentDescription = "",
                            modifier = modifier.wrapContentSize()
                        )
                    }

                    if (!imageRaw.isNullOrEmpty()) {
                        Spacer(modifier = modifier.height(5.dp))
                        AsyncImage(
                            model = imageRaw,
                            contentDescription = "",
                            modifier = modifier
                                .wrapContentSize()
                        )
                    }

                    if (title.isNotEmpty()) {
                        Spacer(modifier = modifier.height(5.dp))
                        Text(
                            text = title,
                            fontSize = 18.nonScaledSp,
                            fontFamily = nanumsquare,
                            fontWeight = FontWeight.Bold,
                            style = TextStyle(
                                color = White,
                                lineHeight = 24.nonScaledSp
                            ),
                            textAlign = TextAlign.Center,
                        )
                    }

                    if (msg.isNotEmpty()) {
                        Spacer(modifier = modifier.height(16.dp))
                        Text(
                            text = msg,
                            fontSize = 14.nonScaledSp,
                            lineHeight = 20.nonScaledSp,
                            fontFamily = nanumsquare,
                            fontWeight = FontWeight.Normal,
                            textAlign = TextAlign.Center,
                            style = YafitTextStyle(
                                color = White,
                            )
                        )
                    }

                    Spacer(modifier = modifier.height(32.dp))

                    when (dialogType) {

                        DialogType.DIALOGTYPE_BUTTON_YES -> {
                            YafitTextButton(
                                text = btn_positive,
                                onClick = {
                                    onClickCallback.invoke(DialogType.DIALOG_CALLBACK_TYPE_POSITIVE)
                                },
                                modifier = modifier
                                    .fillMaxWidth()
                                    .height(40.dp)
                            )
                        }

                        DialogType.DIALOGTYPE_BUTTON_YES_NO -> {
                            Row(
                                modifier = modifier
                                    .height(40.dp)
                                    .fillMaxWidth()
                            ) {
                                YafitTextButton(
                                    text = btn_negative,
                                    textColor = Gray200,
                                    contentColor = Gray500,
                                    onClick = {
                                        onClickCallback.invoke(DialogType.DIALOG_CALLBACK_TYPE_NEGATIVE)
                                    },
                                    modifier = modifier
                                        .fillMaxWidth()
                                        .weight(1f)
                                )
                                Spacer(modifier = modifier.width(8.dp))
                                YafitTextButton(
                                    text = btn_positive,
                                    onClick = {
                                        onClickCallback.invoke(DialogType.DIALOG_CALLBACK_TYPE_POSITIVE)
                                    },
                                    modifier = modifier
                                        .fillMaxWidth()
                                        .weight(1f)
                                )
                            }

                        }

                        else -> {
                            YafitTextButton(
                                text = btn_positive,
                                onClick = {
                                    onClickCallback.invoke(DialogType.DIALOG_CALLBACK_TYPE_POSITIVE)
                                },
                                modifier = modifier
                                    .fillMaxWidth()
                                    .height(40.dp)
                            )
                            Spacer(modifier = modifier.height(4.dp))
                            YafitTextButton(
                                text = btn_negative,
                                textColor = Gray200,
                                contentColor = Gray600,
                                onClick = {
                                    onClickCallback.invoke(DialogType.DIALOG_CALLBACK_TYPE_NEGATIVE)
                                },
                                modifier = modifier
                                    .fillMaxWidth()
                                    .height(40.dp)
                            )
                        }
                    }
                }
            }
        }
    }
}

object DialogType {
    const val DIALOGTYPE_BUTTON_YES = 1
    const val DIALOGTYPE_BUTTON_YES_NO = 2
    const val DIALOGTYPE_BUTTON_YES_NO_HIGHLIGHT = 3

    const val DIALOG_CALLBACK_TYPE_POSITIVE = 0
    const val DIALOG_CALLBACK_TYPE_NEGATIVE = 1
}

2. BottomSheetDialog

우리 프로젝트 환경에선 navigation bar와 BottomSheetDialog가 겹치는 현상이 일어나서 아래처럼 공백을 주었다.

@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
@Composable
fun BlueModalBottomSheet(
    modifier: Modifier,
    onDismissRequest: () -> Unit = {},
    sheetState: SheetState = rememberModalBottomSheetState(),
    contentColor: Color = Gray700,
    content: @Composable ColumnScope.() -> Unit,
) {
    /**
     * ModalBottomSheet에 이거 넣으면 하단 padding 안먹는데 Spacer로 넣으면 떨어짐
     * windowInsets = WindowInsets.navigationBarsIgnoringVisibility
     */

    ModalBottomSheet(
        modifier = modifier.wrapContentHeight(),
        onDismissRequest = { onDismissRequest.invoke() },
        sheetState = sheetState,
        dragHandle = { BottomSheetDefaults.DragHandle() },
        containerColor = contentColor,
    ) {
        Column {
            content()
            Spacer(
                modifier = modifier.windowInsetsBottomHeight(
                    WindowInsets.navigationBarsIgnoringVisibility
                )
            )
        }
    }
}

3. 날짜 선택 Picker

참고 코드 : https://github.com/ChargeMap/Compose-NumberPicker

아래 코드에서 체크 포인트는 윤년 계산과 날짜 선택 완료시 UI에 날짜 포맷을 만드는 부분이다.