feat(#130): add scroll-to-bottom FAB that appears when user scrolls up (#163)

Co-authored-by: M57 <hy4ri@users.noreply.github.com>
This commit is contained in:
M57andM57 authored and GitHub committed 2026-06-20 14:12:25 +03:00
1 parent ca21e22147
commit 4544d88f8d
1 file changed
+34
@@ -13,6 +13,8 @@ import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.BorderStroke
@@ -59,6 +61,7 @@ import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
@@ -73,6 +76,7 @@ import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -109,6 +113,11 @@ fun ChatScreen(
) {
val state by viewModel.uiState.collectAsStateWithLifecycle()
val listState = rememberLazyListState()
val showScrollToBottom by remember {
derivedStateOf {
state.messages.isNotEmpty() && listState.canScrollForward
}
}
var inputText by rememberSaveable { mutableStateOf("") }
val snackbarHostState = remember { SnackbarHostState() }
val isDark = isSystemInDarkTheme()
@@ -522,6 +531,31 @@ fun ChatScreen(
}
}
}
// Scroll-to-bottom FAB
AnimatedVisibility(
visible = showScrollToBottom,
enter = fadeIn() + scaleIn(),
exit = fadeOut() + scaleOut(),
modifier =
Modifier
.align(Alignment.BottomEnd)
.padding(end = 16.dp, bottom = 16.dp),
) {
FloatingActionButton(
onClick = {
listState.animateScrollToItem(state.messages.lastIndex)
},
modifier = Modifier.size(40.dp),
containerColor = MaterialTheme.colorScheme.surfaceContainer,
contentColor = MaterialTheme.colorScheme.onSurface,
) {
Icon(
imageVector = Icons.Filled.KeyboardArrowDown,
contentDescription = "Scroll to bottom",
)
}
}
}
ChatInputBar(