[안드로이드 Jetpack Compose Dialog 시리즈] BasicTextField Dialog 구현하기 #6

2024. 3. 6. 11:30·안드로이드/Compose Dialog
반응형

안녕하세요. 이번 포스팅에서는 안드로이드 Jetpack Compose에서 Text를 입력할 수 있는 BasicTextField를 사용해서 Dialog를 구현하겠습니다. Compose에서는 TextField와 BasicTextField가 있는데 디자인을 커스텀하기 위해서는 BasicTextField를 사용하는 것이 유리하기 때문에 BasicTextField로 Dialog를 만들겠습니다.

안드로이드 compose을 이용해서 만든 text field dialog
안드로이드 compose을 이용해서 만든 text field dialog

1. BasicTextField Dialog 만들기

Compose Material3에서 제공하는 Dialog와 BasicTextField를 사용해서 TextFieldDialog를 만들었습니다. text를 표시하기 위해서 TextState를 관리하는 변수를 만들고, 해당 변수를 BasicTextField의 value와 onValueChange에서 활용해야 합니다. 이렇게 해야 사용자가 입력한 텍스트가 실시간으로 UI에 표시되기 때문입니다.

아래 코드를 보면 BasicTextField에서 decorationBox를 재정의하는 것을 확인할 수 있습니다. decorationBox를 통해서 TextField의 디자인을 원하는 형태로 수정할 수 있기 때문입니다.

// CustomTextFieldDialog.kt
@Composable
fun CustomTextFieldDialog(
    initialText: String?,
    onClickCancel: () -> Unit,
    onClickConfirm: (text: String) -> Unit
) {

    val text = remember { mutableStateOf(initialText ?: "") }

    Dialog(
        onDismissRequest = { onClickCancel() },
    ) {
        Card(
            shape = RoundedCornerShape(8.dp), // Card의 모든 꼭지점에 8.dp의 둥근 모서리 적용
        ) {
            Column(
                modifier = Modifier
                    .width(300.dp)
                    .wrapContentHeight()
                    .background(
                        color = Color.White,
                    )
                    .padding(16.dp),
                horizontalAlignment = Alignment.CenterHorizontally,
            ) {

                Text(text = "후기를 입력해주세요.")

                Spacer(modifier = Modifier.height(15.dp))

                // TextField
                BasicTextField(
                    value = text.value,
                    onValueChange = { text.value = it },
                    singleLine = true,
                    textStyle = TextStyle(
                        color = Color.Black,
                        fontSize = 16.sp,
                        fontWeight = FontWeight.Normal,
                    ),
                    decorationBox = { innerTextField ->
                        Row(
                            modifier = Modifier
                                .fillMaxWidth()
                                .background(
                                    color = Color(0xFFBAE5F5),
                                    shape = RoundedCornerShape(size = 16.dp)
                                )
                                .padding(all = 16.dp),
                            verticalAlignment = Alignment.CenterVertically,
                        ) {
                            Icon(
                                imageVector = Icons.Default.Create,
                                contentDescription = "",
                                tint = Color.DarkGray,
                            )
                            Spacer(modifier = Modifier.width(width = 8.dp))
                            innerTextField()
                        }
                    },
                )

                Spacer(modifier = Modifier.height(15.dp))

                Row(
                    modifier = Modifier.fillMaxWidth(),
                    horizontalArrangement = Arrangement.Center,
                ) {
                    Button(onClick = {
                        onClickCancel()
                    }) {
                        Text(text = "취소")
                    }

                    Spacer(modifier = Modifier.width(5.dp))

                    Button(onClick = {
                        onClickConfirm(text.value)
                    }) {
                        Text(text = "확인")
                    }
                }
            }
        }
    }
}

2. BasicTextField Dialog 사용하기

위에서 만든 BasicTextFieldDialog를 사용하기 위해서 상태를 관리하는 클래스를 만든 후, Screen과 ViewModel에서 비즈니스 로직과 UI 영역을 만들었습니다.

CustomTextFieldDialogState에서는 사용자가 입력한 text를 저장하고 있으며, Dialog에 관련된 기능을 모두 포함하게 구현했습니다.

// CustomTextFieldDialogState.kt
data class CustomTextFieldDialogState(
    var text: String? = null,
    var isShowDialog: Boolean = false,
    val onClickConfirm: (text: String) -> Unit,
    val onClickCancel: () -> Unit,
)


아래 코드는 ViewModel입니다. 위에서 만든 상태 관련 클래스를 MutableState에 저장하고 있으며, Dialog에서 확인을 눌렀을 때 사용자가 입력한 Text를 저장하기 위해서 onClickConfirm 및 onClickCancel 함수에서 text와 isShowDialog에 대한 처리를 하고 있습니다. 

// MainViewModel.kt
@HiltViewModel
class MainViewModel @Inject constructor(
    savedStateHandle: SavedStateHandle?,
) : ViewModel() {

    val customTextFieldDialogState: MutableState<CustomTextFieldDialogState?> =
        mutableStateOf(null)

    init {
        customTextFieldDialogState.value = CustomTextFieldDialogState(
            onClickConfirm = { text ->
                customTextFieldDialogState.value = customTextFieldDialogState.value?.copy(
                    isShowDialog = false,
                    text = text
                )
            },
            onClickCancel = {
                customTextFieldDialogState.value = customTextFieldDialogState.value?.copy(
                    isShowDialog = false
                )
            }
        )
    }

    fun showTextFieldDialog() {
        customTextFieldDialogState.value =
            customTextFieldDialogState.value?.copy(isShowDialog = true)
    }
}


UI를 보여주는 MainScreen 코드입니다. isShowDialog 변수를 통해서 BasicTextFieldDialog 노출 여부를 결정하고 있으며, 사용자가 입력한 텍스트를 버튼 아래에 배치하여 보여주고 있습니다. 사용하려는 목적에 맞게 수정해서 활용하면 좋을 것 같습니다.

// MainScreen.kt
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MainScreen(
    mainNavController: NavHostController,
    viewModel: MainViewModel
) {

    val customTextFieldDialogState = viewModel.customTextFieldDialogState.value

    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text(text = "메인화면") },
                navigationIcon = {
                    IconButton(onClick = { mainNavController.popBackStack() }) {
                        Icon(imageVector = Icons.Default.ArrowBack, contentDescription = null)
                    }
                }
            )
        }
    ) { paddingValues ->
        Column(
            modifier = Modifier.padding(paddingValues)
        ) {
            Button(
                onClick = { viewModel.showTextFieldDialog() },
                shape = RectangleShape,
            ) {
                Text(
                    text = "6. Show TextFieldDialog",
                    textAlign = TextAlign.Center,
                    style = TextStyle(
                        fontSize = 14.sp,
                        fontWeight = FontWeight.Bold
                    )
                )
            }
            Text(
                text = "text: ${viewModel.customTextFieldDialogState.value?.text ?: "텍스트를 입력해주세요."}",
                style = TextStyle(
                    fontSize = 14.sp,
                    fontWeight = FontWeight.Bold
                )
            )

            if (customTextFieldDialogState?.isShowDialog == true) {
                CustomTextFieldDialog(
                    initialText = customTextFieldDialogState.text,
                    onClickCancel = customTextFieldDialogState.onClickCancel,
                    onClickConfirm = customTextFieldDialogState.onClickConfirm
                )
            }
        }
    }
}

3. BasicTextField Dialog 정리

이번 포스팅에서는 안드로이드 Jetpack Compose에서 BasicTextFieldDialog를 구현하고 사용하는 방법에 대해서 알아봤습니다. 구현하는데 어려움은 없었지만 실제로 개발할 때는 TextField의 디자인을 커스텀해야 하는 경우가 많습니다. 그래서 BasicTextField를 사용했으며, decorationBox를 재정의해서 TextField의 디자인을 변경할 수 있었습니다.

이번 과정을 통해서 여러분이 제작하고자 하는 Dialgo와 TextField를 커스텀하는데 도움이 됐으면 좋겠습니다.

저작자표시 비영리 변경금지 (새창열림)

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

[안드로이드 Jetpack Compose Dialog 시리즈] RadioButton Dialog 구현하기 #8  (0) 2024.03.09
[안드로이드 Jetpack Compose Dialog 시리즈] Checkbox Dialog 구현하기 #7  (0) 2024.03.07
[안드로이드 Jetpack Compose Dialog 시리즈] TimePicker Dialog 구현하기 #5  (0) 2024.03.05
[안드로이드 Jetpack Compose Dialog 시리즈] DatePicker Dialog 구현하기 #4  (0) 2024.03.04
[안드로이드 Jetpack Compose Dialog 시리즈] Loading Dialog 구현하기 #3  (0) 2024.03.03
'안드로이드/Compose Dialog' 카테고리의 다른 글
  • [안드로이드 Jetpack Compose Dialog 시리즈] RadioButton Dialog 구현하기 #8
  • [안드로이드 Jetpack Compose Dialog 시리즈] Checkbox Dialog 구현하기 #7
  • [안드로이드 Jetpack Compose Dialog 시리즈] TimePicker Dialog 구현하기 #5
  • [안드로이드 Jetpack Compose Dialog 시리즈] DatePicker Dialog 구현하기 #4
코딩덕
코딩덕
안드로이드, 리액트 등의 개발 노하우와 최신 AI 기술을 다루는 기술 블로그입니다. 실무 중심의 경험을 바탕으로 마주한 문제와 해결 과정을 체계적으로 기록하며, 개발자에게 실질적으로 도움 되는 프로그래밍 팁과 인사이트를 쉽고 명확하게 공유하고자 합니다.
  • 코딩덕
    개발자가 들려주는 IT 이야기
    코딩덕
  • 전체
    오늘
    어제
    • 분류 전체보기 (66)
      • 안드로이드 (62)
        • 멀티 모듈 (11)
        • 클린 아키텍처 (11)
        • 트러블 슈팅 (5)
        • 코틀린 (3)
        • 코루틴 (2)
        • Compose (1)
        • Compose UI (6)
        • Compose Dialog (8)
        • Compose Paging3 (11)
        • Compose State (2)
        • Util (1)
      • Github (3)
        • PR Template (2)
        • AI Code Review (1)
      • 리액트 (1)
        • NextJs (1)
  • 블로그 메뉴

    • 홈
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    안드로이드
    클린 아키텍처
    코틀린
    pager
    jsonadapter
    Gradle
    코루틴
    멀티 모듈
    Jetpack Compose
    트러블슈팅
    sealed class
    MutableState
    UI Layer
    LazyRow
    pr template
    flow
    recyclerview
    Dialog
    ai code review
    enum class
    paging3
    ViewModel
    dynamic json
    Github
    데이터 레이어
    Clean Architecture
    OnBackPressedDispatcher
    ScrollView
    Usecase
    multi module
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
코딩덕
[안드로이드 Jetpack Compose Dialog 시리즈] BasicTextField Dialog 구현하기 #6
상단으로

티스토리툴바