Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package com.cherrish.android.core.designsystem.component.section

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.itemsIndexed
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.cherrish.android.core.designsystem.component.chip.CherrishMissionCard
import com.cherrish.android.core.designsystem.component.chip.CherrishSelectionChip
import com.cherrish.android.core.designsystem.component.type.CherrishSectionChipType
import com.cherrish.android.core.designsystem.theme.CherrishTheme
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf

@Composable
fun CherrishSelectionSection(
title: String,
descriptionTextStyle: TextStyle,
items: ImmutableList<String>,
chipType: CherrishSectionChipType,
onItemClick: (index: Int) -> Unit,
modifier: Modifier = Modifier,
description: String? = null,
selectedIndex: Int? = null
) {
Column(
modifier = modifier.fillMaxWidth()
) {
TitleDescriptionSection(
title = title,
descriptionTextStyle = descriptionTextStyle,
description = description
)

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

SelectionChipGrid(
items = items,
selectedIndex = selectedIndex,
onItemClick = onItemClick,
chipType = chipType
)
}
}

@Composable
private fun SelectionChipGrid(
items: ImmutableList<String>,
chipType: CherrishSectionChipType,
onItemClick: (index: Int) -> Unit,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요거 index 들어간다고 넣어주느거 참 좋타 ㅎ.ㅎ 🫰

modifier: Modifier = Modifier,
selectedIndex: Int? = null
) {
LazyVerticalGrid(
modifier = modifier.fillMaxWidth(),
columns = GridCells.Fixed(2),
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
itemsIndexed(
items = items,
key = { index, item -> "$item-$index" }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 : 요거 걍 index 값으로만 줘도 대지 않나용 ?!

Copy link
Copy Markdown
Contributor Author

@usuuhyn usuuhyn Jan 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

item이랑 섞으면 더 좋다길래 ㅎㅎ ㅎㅎㅎ ㅎㅎ

) { index, text ->
when (chipType) {
CherrishSectionChipType.SELECTION_CHIP -> {
CherrishSelectionChip(
text = text,
onClick = { onItemClick(index) },
modifier = Modifier.fillMaxWidth(),
isSelected = selectedIndex == index
)
}

CherrishSectionChipType.MISSION_CARD -> {
CherrishMissionCard(
text = text,
onClick = { onItemClick(index) },
modifier = Modifier.fillMaxWidth(),
isSelected = selectedIndex == index
)
}
}
}
}
}
Comment on lines +59 to +98
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

LazyVerticalGrid는 스크롤 컨테이너 안에서 런타임 크래시 위험이 커서(중첩 스크롤) 공용 컴포넌트로는 위험합니다.
이 섹션이 LazyColumn/verticalScroll 내부에 들어가면 “무한 높이로 측정” 문제로 크래시 나는 케이스가 흔합니다. userScrollEnabled = false여도 “스크롤러블 레이아웃”인 점은 변하지 않는 경우가 많아서, 공용 컴포넌트라면 비-lazy 2열 레이아웃(아이템 수가 작은 전제) 로 바꾸는 쪽을 추천드립니다.
또한 itemsIndexed(items)key가 없어 리스트가 바뀔 때 선택 상태가 어긋날 여지도 있습니다.

제안 변경안 (중첩 스크롤 안전한 2열 Row/Column 그리드)
@@
-import androidx.compose.foundation.lazy.grid.GridCells
-import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
-import androidx.compose.foundation.lazy.grid.itemsIndexed
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.width
@@
 private fun SelectionChipGrid(
     items: List<String>,
     chipType: CherrishSectionChipType,
     onItemClick: (index: Int) -> Unit,
     modifier: Modifier = Modifier,
     selectedIndex: Int? = null
 ) {
-    LazyVerticalGrid(
-        modifier = modifier.fillMaxWidth(),
-        columns = GridCells.Fixed(2),
-        horizontalArrangement = Arrangement.spacedBy(12.dp),
-        verticalArrangement = Arrangement.spacedBy(12.dp),
-        userScrollEnabled = false
-    ) {
-        itemsIndexed(items) { index, text ->
-            when (chipType) {
-                CherrishSectionChipType.SELECTION_CHIP -> {
-                    CherrishSelectionChip(
-                        text = text,
-                        onClick = { onItemClick(index) },
-                        modifier = Modifier.fillMaxWidth(),
-                        isSelected = selectedIndex == index
-                    )
-                }
-
-                CherrishSectionChipType.MISSION_CARD -> {
-                    CherrishMissionCard(
-                        text = text,
-                        onClick = { onItemClick(index) },
-                        modifier = Modifier.fillMaxWidth(),
-                        isSelected = selectedIndex == index
-                    )
-                }
-            }
-        }
-    }
+    Column(
+        modifier = modifier.fillMaxWidth(),
+        verticalArrangement = Arrangement.spacedBy(12.dp),
+    ) {
+        items.chunked(2).forEachIndexed { rowIndex, row ->
+            Row(
+                modifier = Modifier.fillMaxWidth(),
+                horizontalArrangement = Arrangement.spacedBy(12.dp),
+            ) {
+                row.forEachIndexed { colIndex, text ->
+                    val index = rowIndex * 2 + colIndex
+                    val cellModifier = Modifier.weight(1f)
+                    when (chipType) {
+                        CherrishSectionChipType.SELECTION_CHIP -> CherrishSelectionChip(
+                            text = text,
+                            onClick = { onItemClick(index) },
+                            modifier = cellModifier,
+                            isSelected = selectedIndex == index,
+                        )
+                        CherrishSectionChipType.MISSION_CARD -> CherrishMissionCard(
+                            text = text,
+                            onClick = { onItemClick(index) },
+                            modifier = cellModifier,
+                            isSelected = selectedIndex == index,
+                        )
+                    }
+                }
+                if (row.size == 1) {
+                    Spacer(modifier = Modifier.weight(1f))
+                }
+            }
+        }
+    }
 }
🤖 Prompt for AI Agents
In
@app/src/main/java/com/cherrish/android/core/designsystem/component/section/CherrishSelectionSection.kt
around lines 58 - 95, SelectionChipGrid currently uses LazyVerticalGrid (a
scrollable/lazy container) which can crash when nested in other scrollables and
lacks stable keys; replace the LazyVerticalGrid with a non-lazy two-column
layout built from Column and Rows (e.g., chunk the items into pairs and render
each row with two children) so the component is safe to embed inside other
scroll containers, keep user scrolling disabled by not using any lazy/scrollable
composable, and preserve selection consistency by supplying stable keys for
items (replace itemsIndexed usage with a keyed iteration or ensure keys are
derived from the item value or a stable id) — update SelectionChipGrid, its
itemsIndexed usage, and the places creating rows so CherrishSelectionChip /
CherrishMissionCard rendering remains identical otherwise.


@Composable
private fun TitleDescriptionSection(
title: String,
descriptionTextStyle: TextStyle,
modifier: Modifier = Modifier,
description: String? = null
) {
Column(
modifier = modifier.fillMaxWidth()
) {
Text(
text = title,
style = CherrishTheme.typography.title1SB18,
color = CherrishTheme.colors.gray1000
)

if (!description.isNullOrBlank()) {
Spacer(modifier = Modifier.height(4.dp))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p2: 사이 간격 한 줄 띄어주세요!!
가독성을 위해~~ㅎㅎ

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넹넹 좋아용 ~~


Text(
text = description,
style = descriptionTextStyle,
color = CherrishTheme.colors.gray700
)
}
}
}

@Preview(showBackground = true)
@Composable
private fun SelectionSectionSelectionChipPreview() {
CherrishTheme {
var isSelected by remember { mutableIntStateOf(-1) }

CherrishSelectionSection(
title = "요즘 가장 신경 쓰이는\n피부 고민은 무엇인가요?",
description = "선택한 고민을 기준으로 시술 정보를 정리해줘요.",
descriptionTextStyle = CherrishTheme.typography.body1R14,
items = persistentListOf("여드름 ∙ 트러블", "진정 토너+세럼", "피부결 정돈"),
selectedIndex = isSelected,
onItemClick = { isSelected = it },
chipType = CherrishSectionChipType.SELECTION_CHIP
)
}
}

@Preview(showBackground = true)
@Composable
private fun SelectionSectionMissionCardPreview() {
CherrishTheme {
var isSelected by remember { mutableIntStateOf(-1) }

CherrishSelectionSection(
title = "챌린지 기간 동안\n진행할 미션을 선택해주세요.",
description = "복수 선택이 가능해요.",
descriptionTextStyle = CherrishTheme.typography.body1M14,
items = persistentListOf("반신욕 20분", "진정 토너+세럼", "피부결 정돈", "괄사", "톤 개선", "선크림 바르기"),
selectedIndex = isSelected,
onItemClick = { isSelected = it },
Comment on lines +153 to +158
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Preview 설명과 구현 간 불일치

Preview의 description이 "복수 선택이 가능해요"라고 안내하지만, 현재 구현은 selectedIndex: Int?로 단일 선택만 지원합니다.

실제 요구사항이 복수 선택이라면 selectedIndices: Set<Int> 또는 List<Int>로 변경이 필요하고, 단일 선택이 의도된 것이라면 Preview의 description 텍스트를 수정해야 합니다.

chipType = CherrishSectionChipType.MISSION_CARD
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.cherrish.android.core.designsystem.component.type

enum class CherrishSectionChipType {
SELECTION_CHIP,
MISSION_CARD
}