onVisibilityChanged
Common
Modifier in Compose Ui
Registers a callback to monitor whether or not the node is inside of the viewport of the window or not. Example use cases for this include, auto-playing videos in a feed, logging how long an item was visible, and starting/stopping animations.
Last updated:
Installation
dependencies {
implementation("androidx.compose.ui:ui:1.9.0-alpha04")
}
Overloads
@Stable
fun Modifier.onVisibilityChanged(
@IntRange(from = 0) minDurationMs: Long = 0,
@FloatRange(from = 0.0, to = 1.0) minFractionVisible: Float = 1f,
viewportBounds: LayoutBoundsHolder? = null,
callback: (Boolean) -> Unit,
)
Parameters
name | description |
---|---|
minDurationMs | the amount of time in milliseconds that this node should be considered visible before invoking the callback with (true). Depending on your use case, it might be useful to provide a non-zero number here if it is desirable to avoid triggering the callback on elements during really fast scrolls where they went from visible to invisible in a really short amount of time. |
minFractionVisible | the fraction of the node which should be inside the viewport for the callback to get called with a value of true. A value of 1f means that the entire bounds of the rect need to be inside of the viewport, or that the rect fills 100% of the viewport. A value of 0f means that this will get triggered as soon as a non-zero amount of pixels are inside of the viewport. |
viewportBounds | a reference to the bounds to use as a "viewport" with which to calculate the amount of visibility this element has inside of that viewport. This is most commonly used to account for UI elements such as navigation bars which are drawn on top of the content that this modifier is applied to. It is required that this be passed in to a [layoutBounds] somewhere else in order for this parameter to get used properly. If null is provided, the window of the application will be used as the viewport. |
callback | lambda that is invoked when the fraction of this node inside of the specified viewport crosses the [minFractionVisible]. The boolean argument passed into this lambda will be true in cases where the fraction visible is greater, and false when it is not. |
Code Examples
OnVisibilityChangedAutoplaySample
@Composable
private fun OnVisibilityChangedAutoplaySample() {
@Composable
fun VideoFeed(feedData: List<Video>) {
LazyColumn {
items(feedData) { video ->
VideoRow(
video,
Modifier.onVisibilityChanged(minDurationMs = 500, minFractionVisible = 1f) {
visible ->
if (visible) video.play() else video.pause()
},
)
}
}
}
}
OnVisibilityChangedAutoplayWithViewportSample
@Composable
private fun OnVisibilityChangedAutoplayWithViewportSample() {
@Composable
fun VideoFeed(feedData: List<Video>) {
val viewport = remember { LayoutBoundsHolder() }
LazyColumn(Modifier.layoutBounds(viewport)) {
items(feedData) { video ->
VideoRow(
video,
Modifier.onVisibilityChanged(
minDurationMs = 500,
minFractionVisible = 1f,
viewportBounds = viewport,
) { visible ->
if (visible) video.play() else video.pause()
},
)
}
}
}
}
OnVisibilityChangedDurationLoggingSample
@Composable
private fun OnVisibilityChangedDurationLoggingSample() {
@Composable
fun VideoFeed(feedData: List<Video>, logger: Logger) {
LazyColumn {
items(feedData) { video ->
var startTime by remember { mutableLongStateOf(-1) }
VideoRow(
video,
Modifier.onVisibilityChanged(minDurationMs = 500, minFractionVisible = 1f) {
visible ->
if (visible) {
startTime = System.currentTimeMillis()
} else if (startTime >= 0) {
val durationMs = System.currentTimeMillis() - startTime
logger.logImpression(video.id, durationMs)
startTime = -1
}
},
)
}
}
}
}