Chip은 사용자 인터페이스(UI)에서 작은 정보 그룹을 나타내는 컴포넌트입니다. 보통 해당 컴포넌트의 역할은 다음과 같습니다.
예를 들어, 연락처 정보, 태그, 또는 필터 옵션 등을 나타낼 수 있습니다.
기본적으로 Row Scope를 이용하여 가로 구성이며, Chip을 클릭할 경우 위 사진처럼 해당 Chip 색깔만 바뀌는 UI 요소입니다.
먼저 Chip 상태를 관리하기 위한 State로 구성된 data class를 만들어주었습니다. 내부에는 Chip을 식별하고 구성하는 text 와 선택 상태 여부를 나타내는 isSelected를 포함하여 구성했습니다.
isSelected를 MutableState로 구성한 이유는 지속적으로 상태를 UI에 업데이트 해야하기 때문입니다.
data class ChipState(
var text: String,
val isSelected: MutableState<Boolean>
)
Chip의 디자인의 유연함을 구현하기 위해 selected 값에 따른 색상과 Text Style을 받아오도록 처리하였습니다. 다만 아직 초기 단계이기 때문에 모든 디자인을 커버하지 못한다는 점 참고해주시면 감사하겠습니다.
data class ChipStyle(
val selectedColor: Color,
val unselectedColor: Color,
val chipTextStyle: TextStyle,
val selectedTextColor: Color,
val unselectedTextColor: Color,
val chipModifier: Modifier = Modifier,
)
다음으로는 하나의 Chip의 UI를 구성한 Chip Composable fun 입니다. 유연한 Chip 디자인을 외부에서 적용할 수 있도록 파라메터로 구성하였으며, 하나의 Chip의 경우 Chip Group을 통해서만 접근할 수 있어야 되기 때문에 실질적으로 다른 파일에서 접근하지 못하도록 private 접근 제어자로 설정하였습니다.
@Composable
private fun Chip(
text: String,
selected: Boolean,
selectedColor: Color,
unselectedColor: Color,
chipTextStyle: TextStyle,
selectedTextColor: Color,
unselectedTextColor: Color,
@SuppressLint("ModifierParameter")
chipModifier: Modifier,
modifier: Modifier = Modifier,
onChipClicked: (String, Boolean) -> Unit,
) {
Surface(
color = when {
selected -> selectedColor
else -> unselectedColor
},
shape = RoundedCornerShape(100.dp),
modifier = modifier
) {
Text(
text = text,
color = when {
selected -> selectedTextColor
else -> unselectedTextColor
},
style = chipTextStyle,
modifier = chipModifier
.clickable { onChipClicked(text, selected) }
)
}
}
Surface를 활용하여 외관 디자인을 구성하였으며, Text를 통해 Chip의 text value를 표현할 수 있도록 구성하였습니다.
실질적으로 Chip을 활용할 수 있는 Chpi Group을 Chpis라는 Composable 함수로 구현하였습니다.
구현 방식은 다음과 같습니다.
@Composable
fun Chips(
modifier: Modifier = Modifier,
elements: List<ChipState>,
chipStyle: ChipStyle,
onChipClicked: (String, Boolean, Int) -> Unit,
) {
LazyRow(modifier = modifier) {
items(elements.size) { idx ->
Chip(
text = elements[idx].text,
selected = elements[idx].isSelected.value,
selectedColor = chipStyle.selectedColor,
unselectedColor = chipStyle.unselectedColor,
chipTextStyle = chipStyle.chipTextStyle,
selectedTextColor = chipStyle.selectedTextColor,
unselectedTextColor = chipStyle.unselectedTextColor,
chipModifier = chipStyle.chipModifier,
onChipClicked = { content, isSelected ->
onChipClicked(content, isSelected, idx)
}
)
Spacer(modifier = Modifier.padding(8.dp))
}
}
}
실제로 Chips를 활용하는 예시를 보여드리겠습니다.
아래 예제에서는 ViewModel 에서 받아온 SnapshotStateList 를 활용해 ChipState의 List를 받아와 Chips 의 elements에 넣어주고 있습니다.
ChipState의 isSelected의 경우 위 설명과 동일하게 즉각적인 UI 업데이트를 위해 MutableState를 활용하였습니다.
Chip이 클릭된 경우 ViewModel에서는 isSelected 값을 true로 변경해주고 이외의 모든 ChipState는 false로 만들어 주며 Chip의 선택됨을 UI에 업데이트합니다.
val chipStyle: ChipStyle = ChipStyle(
selectedColor = RED_LIGHT,
unselectedColor = Grey2,
chipTextStyle = Typography.bodySmall,
selectedTextColor = Main100,
unselectedTextColor = Grey7,
chipModifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp)
)
@Composable
fun SaveScreen(
viewModel: SaveScreenViewModel = hiltViewModel(),
) {
val viewState by viewModel.viewState.collectAsState()
Scaffold(
topBar = {
SaveTopBar()
}
) { innerPadding ->
Chips(
modifier = Modifier
.padding(start = 20.dp)
.padding(innerPadding),
elements = viewState.chipElements,
chipStyle = chipStyle,
onChipClicked = { _, _, chipIndex ->
viewModel.setEvent(SaveEvent.OnChipClicked(chipIndex))
}
)
EmptyView(modifier = Modifier.fillMaxSize())
}
}
// 아래는 ViewModel 로직입니다. (Orbit MVI 사용)
val chipElements: SnapshotStateList<ChipState> = mutableStateListOf(
ChipState("전체", mutableStateOf(true)),
ChipState("밥집", mutableStateOf(false)),
ChipState("카페", mutableStateOf(false)),
ChipState("술", mutableStateOf(false))
)
override fun handleEvents(event: SaveEvent) {
when (event) {
is SaveEvent.OnChipClicked -> {
updateState {
chipElements.mapIndexed { index, chipState ->
chipState.isSelected.value = index == event.chipIndex
}
copy(
chipElements = chipElements
)
}
}
}
}
해당 로직은 SDK로 배포하여 누구나 쉽게 쓸 수 있도록 할 예정입니다 많은 관심 부탁드려요🌟
좋은 글 감사합니다 !