Introducing our new Modern UI Kit built for Compose & Kotlin Check it out

onFirstVisible

Common

Modifier in Compose Ui

Registers a callback to monitor when the node is first inside of the viewport of the window or not. Example use cases for this include impression and view counting, starting animations, or doing work that is only required once the item is visible to the user.

Last updated:

Installation

dependencies {
   implementation("androidx.compose.ui:ui:1.9.0-alpha04")
}

Overloads

@Stable
fun Modifier.onFirstVisible(
    @IntRange(from = 0) minDurationMs: Long = 0,
    @FloatRange(from = 0.0, to = 1.0) minFractionVisible: Float = 1f,
    viewportBounds: LayoutBoundsHolder? = null,
    callback: () -> Unit,
)

Parameters

namedescription
minDurationMsthe amount of time in milliseconds that this node should be considered visible before invoking the callback. 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 are only visible for a short amount of time.
minFractionVisiblethe fraction of the node which should be inside the viewport to be considered visible. 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.
viewportBoundsa 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.
callbacklambda that is invoked when the fraction of this node inside of the specified viewport is greater than minFractionVisible. This lambda will only get invoked a maximum of one time while the element is attached.

Code Examples

OnFirstVisibleImpressionLoggingSample

@Composable
private fun OnFirstVisibleImpressionLoggingSample() {
    @Composable
    fun VideoFeed(feedData: List<Video>, logger: Logger) {
        LazyColumn {
            items(feedData) { video ->
                VideoRow(
                    video,
                    Modifier.onFirstVisible(minDurationMs = 500, minFractionVisible = 1f) {
                        logger.logImpression(video.id)
                    },
                )
            }
        }
    }
}

OnFirstVisibleImpressionLoggingWithViewportSample

@Composable
private fun OnFirstVisibleImpressionLoggingWithViewportSample() {
    @Composable
    fun VideoFeed(feedData: List<Video>, logger: Logger) {
        val viewport = remember { LayoutBoundsHolder() }
        LazyColumn(Modifier.layoutBounds(viewport)) {
            items(feedData) { video ->
                VideoRow(
                    video,
                    Modifier.onFirstVisible(
                        minDurationMs = 500,
                        minFractionVisible = 1f,
                        viewportBounds = viewport,
                    ) {
                        logger.logImpression(video.id)
                    },
                )
            }
        }
    }
}
by @alexstyl