ursus
07/15/2025, 8:11 PMJetchat
sample
I want to keep scroll at 0 (if it was at 0) when new messages are inserted
val listState = rememberLazyListState()
SideEffect {
if (!listState.canScrollBackward) {
listState.requestScrollToItem(index = 0) <------------------
}
}
LazyColumn(
...
state = listState,
...
) {
...
}
this seems to do the trick.
However, I also have a next screen (image message detail). If I navigate there, and then back, the chat screen composable is "instantiated" again, therefore triggering SideEffect
lambda, and resetting the scroll -- which is not what I want -- the scroll offset should be kept (which it does by default, but then I don't get that "keep scroll at 0" behavior)
Any clean way around this other than hacks?Bogdan Vladoiu Lbs
07/15/2025, 8:19 PMval uiScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
fun ui(block: suspend CoroutineScope.() -> Unit) = uiScope.launch(block)
try replacing the effect you have with the ui{ } dsl above. see if it fires only once and if it fixes your problemursus
07/15/2025, 8:22 PMui
instead of SideEffect
, then it also gets executed, no?Bogdan Vladoiu Lbs
07/15/2025, 8:24 PMBogdan Vladoiu Lbs
07/15/2025, 8:24 PMBogdan Vladoiu Lbs
07/15/2025, 8:25 PMursus
07/15/2025, 8:26 PMBogdan Vladoiu Lbs
07/15/2025, 8:37 PMursus
07/15/2025, 8:48 PMlistState.requestScrollToItem
requires, to run on each compositionursus
07/15/2025, 8:48 PMursus
07/15/2025, 8:49 PMBogdan Vladoiu Lbs
07/15/2025, 10:12 PMAlex Vanyo
07/15/2025, 11:32 PM1px
high item as your bottom-most item, then when you are completely scrolled to the bottom, any new messages that come in will slot in right above that bottom-most item empty item, and you’ll naturally stay pinned to the bottom and you shouldn’t need any direct scroll calls.
If you scroll away from the bottom (and therefore the scroll position is at any other point in the list) then you’ll keep that position when new items show up.ursus
07/15/2025, 11:36 PMAlex Vanyo
07/15/2025, 11:39 PMitem
, but one that exists just to take a tiny amount of space for this behavior. I’ve done something like this:
item(key = "KeepScrolledToBottom") {
// Trick to keep scrolled to the bottom only if we are at the bottom
// Having a 1px high item at the very start (therefore bottom with reverseLayout)
// will be the current scroll position if and only if we are scrolled to the
// very bottom
Spacer(Modifier.height(with(LocalDensity.current) { 1f.toDp() }))
}
items(inputEventInfos.reversed(), key = InputEventInfo::id) {
// actual items
}
ursus
07/15/2025, 11:40 PMAlex Vanyo
07/15/2025, 11:46 PMvar isFirstComposition by remember { mutablesStateOf(true) }
that you set to false
at an appropriate time laterursus
07/15/2025, 11:49 PMAlex Vanyo
07/15/2025, 11:50 PMrememberLazyListState()
uses rememberSaveable
under the hood, which allows persisting state through navigation happeningursus
07/15/2025, 11:52 PM