commonMain.androidx.compose.foundation.Overscroll.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of foundation-desktop Show documentation
Show all versions of foundation-desktop Show documentation
Higher level abstractions of the Compose UI primitives. This library is design system agnostic, providing the high-level building blocks for both application and design-system developers
/*
* Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.compose.foundation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.unit.Velocity
/**
* An OverscrollEffect represents a visual effect that displays when the edges of a scrolling
* container have been reached with a scroll or fling. For the default platform effect that should
* be used in most cases, see
* [androidx.compose.foundation.gestures.ScrollableDefaults.overscrollEffect].
*
* OverscrollEffect conceptually 'decorates' scroll / fling events: consuming some of the delta or
* velocity before and/or after the event is consumed by the scrolling container. [applyToScroll]
* applies overscroll to a scroll event, and [applyToFling] applies overscroll to a fling.
*
* Higher level components such as [androidx.compose.foundation.lazy.LazyColumn] will automatically
* configure an OverscrollEffect for you. To use a custom OverscrollEffect you first need to
* provide it with scroll and/or fling events - usually by providing it to a
* [androidx.compose.foundation.gestures.scrollable]. Then you can draw the effect on top of the
* scrolling content using [Modifier.overscroll].
*
* @sample androidx.compose.foundation.samples.OverscrollSample
*/
@ExperimentalFoundationApi
@Stable
interface OverscrollEffect {
/**
* Applies overscroll to [performScroll]. [performScroll] should represent a drag / scroll, and
* returns the amount of delta consumed, so in simple cases the amount of overscroll to show
* should be equal to `delta - performScroll(delta)`. The OverscrollEffect can optionally
* consume some delta before calling [performScroll], such as to release any existing tension.
* The implementation *must* call [performScroll] exactly once. This function should return the
* sum of all the delta that was consumed during this operation - both by the overscroll and
* [performScroll].
*
* For example, assume we want to apply overscroll to a custom component that isn't using
* [androidx.compose.foundation.gestures.scrollable]. Here is a simple example of a component
* using [androidx.compose.foundation.gestures.draggable] instead:
*
* @sample androidx.compose.foundation.samples.OverscrollWithDraggable_Before
*
* To apply overscroll, we need to decorate the existing logic with applyToScroll, and
* return the amount of delta we have consumed when updating the drag position. Note that we
* also need to call applyToFling - this is used as an end signal for overscroll so that effects
* can correctly reset after any animations, when the gesture has stopped.
*
* @sample androidx.compose.foundation.samples.OverscrollWithDraggable_After
*
* @param delta total scroll delta available
* @param source the source of the delta
* @param performScroll the scroll action that the overscroll is applied to. The [Offset]
* parameter represents how much delta is available, and the return value is how much delta was
* consumed. Any delta that was not consumed should be used to show the overscroll effect.
* @return the delta consumed from [delta] by the operation of this function - including that
* consumed by [performScroll].
*/
fun applyToScroll(
delta: Offset,
source: NestedScrollSource,
performScroll: (Offset) -> Offset
): Offset
/**
* Applies overscroll to [performFling]. [performFling] should represent a fling (the release
* of a drag or scroll), and returns the amount of [Velocity] consumed, so in simple cases the
* amount of overscroll to show should be equal to `velocity - performFling(velocity)`. The
* OverscrollEffect can optionally consume some [Velocity] before calling [performFling], such
* as to release any existing tension. The implementation *must* call [performFling] exactly
* once.
*
* For example, assume we want to apply overscroll to a custom component that isn't using
* [androidx.compose.foundation.gestures.scrollable]. Here is a simple example of a component
* using [androidx.compose.foundation.gestures.draggable] instead:
*
* @sample androidx.compose.foundation.samples.OverscrollWithDraggable_Before
*
* To apply overscroll, we decorate the existing logic with applyToScroll, and return the amount
* of delta we have consumed when updating the drag position. We then call applyToFling using
* the velocity provided by onDragStopped.
*
* @sample androidx.compose.foundation.samples.OverscrollWithDraggable_After
*
* @param velocity total [Velocity] available
* @param performFling the [Velocity] consuming lambda that the overscroll is applied to. The
* [Velocity] parameter represents how much [Velocity] is available, and the return value is how
* much [Velocity] was consumed. Any [Velocity] that was not consumed should be used to show the
* overscroll effect.
*/
suspend fun applyToFling(velocity: Velocity, performFling: suspend (Velocity) -> Velocity)
/**
* Whether this OverscrollEffect is currently displaying overscroll.
*
* @return true if this OverscrollEffect is currently displaying overscroll
*/
val isInProgress: Boolean
/**
* A [Modifier] that will draw this OverscrollEffect
*/
val effectModifier: Modifier
}
/**
* Renders overscroll from the provided [overscrollEffect].
*
* This modifier is a convenience method to call [OverscrollEffect.effectModifier], which
* renders the actual effect. Note that this modifier is only responsible for the visual part of
* overscroll - on its own it will not handle input events. In addition to using this modifier you
* also need to propagate events to the [overscrollEffect], most commonly by using a
* [androidx.compose.foundation.gestures.scrollable].
*
* @sample androidx.compose.foundation.samples.OverscrollSample
*
* @param overscrollEffect the [OverscrollEffect] to render
*/
@ExperimentalFoundationApi
fun Modifier.overscroll(overscrollEffect: OverscrollEffect): Modifier =
this.then(overscrollEffect.effectModifier)
@OptIn(ExperimentalFoundationApi::class)
@Composable
internal expect fun rememberOverscrollEffect(): OverscrollEffect
@OptIn(ExperimentalFoundationApi::class)
internal object NoOpOverscrollEffect : OverscrollEffect {
override fun applyToScroll(
delta: Offset,
source: NestedScrollSource,
performScroll: (Offset) -> Offset
): Offset = performScroll(delta)
override suspend fun applyToFling(
velocity: Velocity,
performFling: suspend (Velocity) -> Velocity
) { performFling(velocity) }
override val isInProgress: Boolean
get() = false
override val effectModifier: Modifier
get() = Modifier
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy