@@ -57,8 +57,10 @@ import androidx.compose.ui.layout.positionInRoot
5757import androidx.compose.ui.platform.LocalDensity
5858import androidx.compose.ui.platform.LocalFocusManager
5959import androidx.compose.ui.platform.LocalSoftwareKeyboardController
60+ import androidx.compose.ui.text.rememberTextMeasurer
6061import androidx.compose.ui.text.style.TextAlign
6162import androidx.compose.ui.text.style.TextDecoration
63+ import androidx.compose.ui.unit.Constraints
6264import androidx.compose.ui.unit.Density
6365import androidx.compose.ui.unit.dp
6466import com.nexters.emotia.core.designsystem.component.ChatBubble
@@ -263,6 +265,25 @@ private fun ChatConversationScreen(
263265 val colors = LocalEmotiaColors .current
264266 val density = LocalDensity .current
265267 val imeHeight = WindowInsets .ime.getBottom(density)
268+ val textMeasurer = rememberTextMeasurer()
269+
270+ // 마지막 메시지의 최종 높이 계산
271+ val lastMessageFinalHeight = remember(uiState.messages.lastOrNull()?.text) {
272+ val lastMessage = uiState.messages.lastOrNull()
273+ if (lastMessage != null ) {
274+ val textLayoutResult = textMeasurer.measure(
275+ text = lastMessage.text,
276+ constraints = Constraints (
277+ maxWidth = with (density) { (400 .dp - 32 .dp).toPx().toInt() } // 말풍선 최대 너비 - 패딩
278+ )
279+ )
280+ with (density) {
281+ (textLayoutResult.size.height + 60 .dp.toPx()).toDp() // 텍스트 높이 + 말풍선 패딩
282+ }
283+ } else {
284+ 0 .dp
285+ }
286+ }
266287
267288 Column (
268289 modifier = modifier
@@ -286,48 +307,56 @@ private fun ChatConversationScreen(
286307 CircularProgressIndicator (color = colors.primaryLight)
287308 }
288309 } else {
289- LazyColumn (
290- state = lazyListState,
291- modifier = Modifier .weight(1f ),
292- contentPadding = PaddingValues (
293- top = 8 .dp,
294- bottom = if (isKeyboardVisible) {
295- with (density) { imeHeight.toDp() }
296- } else {
297- 8 .dp
310+ Box (modifier = Modifier .weight(1f )) {
311+ LazyColumn (
312+ state = lazyListState,
313+ modifier = Modifier .fillMaxSize(),
314+ contentPadding = PaddingValues (top = 8 .dp, bottom = 8 .dp),
315+ verticalArrangement = Arrangement .spacedBy(12 .dp)
316+ ) {
317+ itemsIndexed(
318+ items = uiState.messages,
319+ key = { _, message -> message.timestamp }
320+ ) { index, message ->
321+ ChatBubble (
322+ text = message.text,
323+ type = message.type,
324+ messageId = message.timestamp.toString(),
325+ skipTypewriterEffect = index <= 1
326+ )
298327 }
299- ),
300- verticalArrangement = Arrangement .spacedBy(12 .dp)
301- ) {
302- itemsIndexed(
303- items = uiState.messages,
304- key = { _, message -> message.timestamp }
305- ) { index, message ->
306- ChatBubble (
307- text = message.text,
308- type = message.type,
309- messageId = message.timestamp.toString(),
310- skipTypewriterEffect = index <= 1
311- )
312- }
313328
314- if (uiState.isLoading && uiState.messages.isNotEmpty()) {
315- item {
316- Box (
317- modifier = Modifier
318- .wrapContentWidth()
319- .padding(start = 16 .dp, bottom = 4 .dp),
320- contentAlignment = Alignment .CenterStart
321- ) {
322- TypingIndicator (
323- modifier = Modifier .height(40 .dp)
329+ if (uiState.isLoading && uiState.messages.isNotEmpty()) {
330+ item {
331+ Box (
332+ modifier = Modifier
333+ .wrapContentWidth()
334+ .padding(start = 16 .dp, bottom = 4 .dp),
335+ contentAlignment = Alignment .CenterStart
336+ ) {
337+ TypingIndicator (
338+ modifier = Modifier .height(40 .dp)
339+ )
340+ }
341+ }
342+ }
343+
344+ // 키보드 높이 / 마지막 메시지 높이 중 더 큰 값을 아래 여백으로 추가 (애니메이션에 따른 대응)
345+ if (isKeyboardVisible) {
346+ item {
347+ val safetyMargin = 50 .dp
348+ val keyboardPadding = maxOf(
349+ with (density) { imeHeight.toDp() },
350+ lastMessageFinalHeight + safetyMargin
324351 )
352+ Spacer (modifier = Modifier .height(keyboardPadding))
325353 }
326354 }
355+
327356 }
357+
328358 }
329359 }
330-
331360 EmotiaChatTextField (
332361 value = uiState.currentInputText,
333362 onValueChange = { text ->
0 commit comments