Omkar Amberkar
01/11/2024, 2:08 PMtrue
. The composition is in viewport when rendered which is why the value is true
in first place and the initial value of it is false
.
Expected behavior is that it only gets triggered once right away when its displayed and not twice for the same value true
var isInViewport by remember { mutableStateOf(false) }
Box(modifier.onViewport { inViewport, componentPosition ->
isInViewport = inViewport
currentComponentPosition = componentPosition
})
LaunchedEffect(isInViewport) {
if (isInViewport) {
uim.onDisplay?.invoke(currentComponentPosition)
} else {
uim.onOutOfViewport?.invoke(null)
}
}
Pablichjenkov
01/11/2024, 2:23 PMOmkar Amberkar
01/11/2024, 2:37 PMPablichjenkov
01/11/2024, 2:47 PMFabricio Vergara
01/11/2024, 2:51 PMvar lastIsInViewport by rememberSaveable { mutableStateOf(!isInViewport) }
LaunchedEffect(isInViewport) {
if(lastIsInViewport != isInViewport) {
lastIsInViewport = isInViewport
// code
}
}
Omkar Amberkar
01/11/2024, 2:52 PMPablichjenkov
01/11/2024, 3:40 PMOmkar Amberkar
01/11/2024, 3:49 PMOmkar Amberkar
01/11/2024, 3:52 PMZach Klippenstein (he/him) [MOD]
01/11/2024, 4:58 PMOmkar Amberkar
01/11/2024, 6:59 PMonViewport
is our own custom modifier that looks like below which helps us to get the view's position in the window
onGloballyPositioned { coordinates ->
isInViewport = coordinates.isInViewport()
val (x: Int, y: Int, width: Int, height: Int) = when {
isInViewport -> with(coordinates.positionInRoot()) {
listOf(x.toInt(), y.toInt(), coordinates.size.width, coordinates.size.height)
}
else -> listOf(0, 0, 0, 0)
}
componentPosition = ComponentPosition(
topLeftX = x,
topLeftY = y,
width = width,
height = height
)
}
Zach Klippenstein (he/him) [MOD]
01/11/2024, 7:09 PMonGloballyPositioned
happens even after the layout passOmkar Amberkar
01/11/2024, 7:12 PMfun Modifier.onViewport(
onDisplayedInViewport: (Boolean, ComponentPosition) -> Unit,
): Modifier = composed {
var isInViewport by remember { mutableStateOf(false) }
var isDisplayed by remember { mutableStateOf(false) }
var componentPosition by remember { mutableStateOf(ComponentPosition.Default) }
LaunchedEffect(isInViewport, isDisplayed, onDisplayedInViewport) {
if (!isInViewport) {
isDisplayed = false
onDisplayedInViewport(isDisplayed, componentPosition)
} else if (!isDisplayed) {
isDisplayed = true
onDisplayedInViewport(isDisplayed, componentPosition)
}
}
onGloballyPositioned { coordinates ->
isInViewport = coordinates.isInViewport()
val (x: Int, y: Int, width: Int, height: Int) = when {
isInViewport -> with(coordinates.positionInRoot()) {
listOf(x.toInt(), y.toInt(), coordinates.size.width, coordinates.size.height)
}
else -> listOf(0, 0, 0, 0)
}
componentPosition = ComponentPosition(
topLeftX = x,
topLeftY = y,
width = width,
height = height
)
}
}
for a mofidier inside a column hosted by lazyColumn
as such
onViewport { inViewport, componentPosition ->
isInViewport = inViewport
currentComponentPosition = componentPosition
}
which results in LaunchedEffect called twice in the below case
var isInViewport by remember { mutableStateOf(false) }
Box(Modifier.
onViewport { inViewport, componentPosition ->
isInViewport = inViewport
currentComponentPosition = componentPosition
}
)
LaunchedEffect(isInViewport) {
if (isInViewport) {
uim.onDisplay?.invoke(currentComponentPosition)
} else {
uim.onOutOfViewport?.invoke(null)
}
}
Omkar Amberkar
01/11/2024, 7:13 PMZach Klippenstein (he/him) [MOD]
01/11/2024, 8:18 PMisInViewport=false
on the first composition. Then you’ll do the first layout pass, get the onGloballyPositioned
callback, which may set isInViewport
to true, which will cause a recomposition on the next frame where you’ll see the new value. If the component is not in view immediately, then it shouldn’t recompose again until it comes into view.Zach Klippenstein (he/him) [MOD]
01/11/2024, 8:20 PMLaunchedEffect(uim) {
snapshotFlow { isInViewport }.collect { isInViewport ->
if (isInViewport) {
…
}
}
louiscad
01/17/2024, 3:59 PMsnapshotFlow
instead of a LaunchedEffect
key?Zach Klippenstein (he/him) [MOD]
01/17/2024, 4:10 PMlouiscad
01/17/2024, 4:19 PM