OneHandedGestureScrollIndicator

Functions summary

Unit
@Composable
OneHandedGestureScrollIndicator(
    gestureConfiguration: OneHandedGestureConfiguration,
    indicatorState: OneHandedGestureScrollIndicatorState,
    scrollState: ScalingLazyListState,
    modifier: Modifier,
    scrollIndicatorColors: ScrollIndicatorColors,
    gestureIndicatorTint: Color,
    gestureIndicatorBackgroundColor: Color,
    reverseDirection: Boolean,
    positionAnimationSpec: AnimationSpec<Float>
)

A scroll indicator that transitions to indicate that a scroll gesture is available to the user.

Unit
@Composable
OneHandedGestureScrollIndicator(
    gestureConfiguration: OneHandedGestureConfiguration,
    indicatorState: OneHandedGestureScrollIndicatorState,
    scrollState: TransformingLazyColumnState,
    modifier: Modifier,
    scrollIndicatorColors: ScrollIndicatorColors,
    gestureIndicatorTint: Color,
    gestureIndicatorBackgroundColor: Color,
    reverseDirection: Boolean,
    positionAnimationSpec: AnimationSpec<Float>
)

A scroll indicator that transitions to indicate that a scroll gesture is available to the user.

Functions

OneHandedGestureScrollIndicator

@Composable
fun OneHandedGestureScrollIndicator(
    gestureConfiguration: OneHandedGestureConfiguration,
    indicatorState: OneHandedGestureScrollIndicatorState,
    scrollState: ScalingLazyListState,
    modifier: Modifier = Modifier,
    scrollIndicatorColors: ScrollIndicatorColors = ScrollIndicatorDefaults.colors(),
    gestureIndicatorTint: Color = OneHandedGestureDefaults.scrollIndicatorTint,
    gestureIndicatorBackgroundColor: Color = OneHandedGestureDefaults.scrollIndicatorBackgroundColor,
    reverseDirection: Boolean = false,
    positionAnimationSpec: AnimationSpec<Float> = ScrollIndicatorDefaults.PositionAnimationSpec
): Unit

A scroll indicator that transitions to indicate that a scroll gesture is available to the user.

This component functions as a standard scroll indicator, reflecting the scroll position of a androidx.wear.compose.foundation.lazy.ScalingLazyColumn. It also observes the OneHandedGestureScrollIndicatorState to manage the visual transition into a gesture indicator. When OneHandedGestureScrollIndicatorState.showIndicator is called, the indicator temporarily replaces its standard visual state with a gesture animation sequence.

Sample demonstrating a gesture indicator applied to a androidx.wear.compose.foundation.lazy.ScalingLazyColumn:

import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.scrollable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.rememberOverscrollEffect
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
import androidx.wear.compose.material3.Button
import androidx.wear.compose.material3.EdgeButton
import androidx.wear.compose.material3.ScreenScaffold
import androidx.wear.compose.material3.Text
import androidx.wear.compose.material3.onehandedgesture.GestureAction
import androidx.wear.compose.material3.onehandedgesture.GesturePriority
import androidx.wear.compose.material3.onehandedgesture.OneHandedGestureClickIndicator
import androidx.wear.compose.material3.onehandedgesture.OneHandedGestureClickIndicatorState
import androidx.wear.compose.material3.onehandedgesture.OneHandedGestureDefaults
import androidx.wear.compose.material3.onehandedgesture.OneHandedGestureScrollIndicator
import androidx.wear.compose.material3.onehandedgesture.OneHandedGestureScrollIndicatorState
import androidx.wear.compose.material3.onehandedgesture.oneHandedGesture
import androidx.wear.compose.material3.onehandedgesture.rememberOneHandedGestureConfiguration

val backDispatcherOwner = LocalOnBackPressedDispatcherOwner.current
val onClick =
    remember<() -> Unit> { { backDispatcherOwner?.onBackPressedDispatcher?.onBackPressed() } }
val scrollState = rememberScalingLazyListState()
val coroutineScope = rememberCoroutineScope()

val buttonInteractionSource = remember { MutableInteractionSource() }
val buttonGestureConfig =
    rememberOneHandedGestureConfiguration(
        action = GestureAction.Primary,
        priority = GesturePriority.Clickable,
    )
val buttonIndicatorState = remember { OneHandedGestureClickIndicatorState() }

val scrollGestureConfig =
    rememberOneHandedGestureConfiguration(
        action = GestureAction.Primary,
        priority = GesturePriority.Scrollable,
    )
val scrollIndicatorState =
    remember(scrollGestureConfig) { OneHandedGestureScrollIndicatorState() }

ScreenScaffold(
    scrollState = scrollState,
    edgeButton = {
        EdgeButton(
            onClick = onClick,
            interactionSource = buttonInteractionSource,
            modifier =
                if (scrollState.canScrollForward) {
                    Modifier
                } else {
                    // Apply the one-handed gesture modifier only when the container cannot
                    // scroll further, ensuring the EdgeButton is fully visible and interactive
                    Modifier.oneHandedGesture(
                        gestureConfiguration = buttonGestureConfig,
                        interactionSource = buttonInteractionSource,
                        onGestureLabel = "close",
                        onGestureAvailable = {
                            coroutineScope.launch { buttonIndicatorState.showIndicator() }
                        },
                        onGesture = onClick,
                    )
                } then
                    Modifier.scrollable(
                        state = scrollState,
                        orientation = Orientation.Vertical,
                        reverseDirection = true,
                        overscrollEffect = rememberOverscrollEffect(),
                    ),
        ) {
            OneHandedGestureClickIndicator(buttonGestureConfig, buttonIndicatorState) {
                Text("Close")
            }
        }
    },
    scrollIndicator = {
        OneHandedGestureScrollIndicator(
            gestureConfiguration = scrollGestureConfig,
            indicatorState = scrollIndicatorState,
            scrollState = scrollState,
            modifier = Modifier.align(Alignment.CenterEnd),
        )
    },
) { contentPadding ->
    ScalingLazyColumn(
        state = scrollState,
        contentPadding = contentPadding,
        modifier =
            Modifier.fillMaxSize()
                .oneHandedGesture(
                    gestureConfiguration = scrollGestureConfig,
                    onGestureLabel = "scroll",
                    onGestureAvailable = {
                        coroutineScope.launch { scrollIndicatorState.showIndicator() }
                    },
                    onGesture = { OneHandedGestureDefaults.scrollDown(scrollState) },
                ),
        autoCentering = null,
    ) {
        items(10) { Text("Item $it") }
    }
}
Parameters
gestureConfiguration: OneHandedGestureConfiguration

the specification for the one-handed gesture

indicatorState: OneHandedGestureScrollIndicatorState

The state object used to synchronize the indicator visibility.

scrollState: ScalingLazyListState

The state object of the androidx.wear.compose.foundation.lazy.ScalingLazyColumn this indicator is coupled with.

modifier: Modifier = Modifier

The Modifier to be applied to the scroll indicator.

scrollIndicatorColors: ScrollIndicatorColors = ScrollIndicatorDefaults.colors()

ScrollIndicatorColors that will be used to resolve the indicator and track colors for this androidx.wear.compose.material3.ScrollIndicator.

gestureIndicatorTint: Color = OneHandedGestureDefaults.scrollIndicatorTint

The color which will be used for a tint of the gesture animation icon.

gestureIndicatorBackgroundColor: Color = OneHandedGestureDefaults.scrollIndicatorBackgroundColor

The color which will be used for a background behind the gesture animation.

reverseDirection: Boolean = false

Reverses direction of ScrollIndicator if true.

positionAnimationSpec: AnimationSpec<Float> = ScrollIndicatorDefaults.PositionAnimationSpec

AnimationSpec for position animation. The Position animation is used for animating changes to the scroll size and position. To disable this animation androidx.compose.animation.core.snap AnimationSpec should be passed instead.

See also the UI Design Guides for One-Handed Gestures

OneHandedGestureScrollIndicator

@Composable
fun OneHandedGestureScrollIndicator(
    gestureConfiguration: OneHandedGestureConfiguration,
    indicatorState: OneHandedGestureScrollIndicatorState,
    scrollState: TransformingLazyColumnState,
    modifier: Modifier = Modifier,
    scrollIndicatorColors: ScrollIndicatorColors = ScrollIndicatorDefaults.colors(),
    gestureIndicatorTint: Color = OneHandedGestureDefaults.scrollIndicatorTint,
    gestureIndicatorBackgroundColor: Color = OneHandedGestureDefaults.scrollIndicatorBackgroundColor,
    reverseDirection: Boolean = false,
    positionAnimationSpec: AnimationSpec<Float> = ScrollIndicatorDefaults.PositionAnimationSpec
): Unit

A scroll indicator that transitions to indicate that a scroll gesture is available to the user.

This component functions as a standard scroll indicator, reflecting the scroll position of a androidx.wear.compose.foundation.lazy.TransformingLazyColumn. It also observes the OneHandedGestureScrollIndicatorState to manage the visual transition into a gesture indicator. When OneHandedGestureScrollIndicatorState.showIndicator is called, the indicator temporarily replaces its standard visual state with a gesture animation sequence.

Sample demonstrating a gesture indicator applied to a androidx.wear.compose.foundation.lazy.TransformingLazyColumn:

import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.scrollable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.rememberOverscrollEffect
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.wear.compose.foundation.lazy.TransformingLazyColumn
import androidx.wear.compose.foundation.lazy.rememberTransformingLazyColumnState
import androidx.wear.compose.material3.Button
import androidx.wear.compose.material3.EdgeButton
import androidx.wear.compose.material3.ScreenScaffold
import androidx.wear.compose.material3.Text
import androidx.wear.compose.material3.onehandedgesture.GestureAction
import androidx.wear.compose.material3.onehandedgesture.GesturePriority
import androidx.wear.compose.material3.onehandedgesture.OneHandedGestureClickIndicator
import androidx.wear.compose.material3.onehandedgesture.OneHandedGestureClickIndicatorState
import androidx.wear.compose.material3.onehandedgesture.OneHandedGestureDefaults
import androidx.wear.compose.material3.onehandedgesture.OneHandedGestureScrollIndicator
import androidx.wear.compose.material3.onehandedgesture.OneHandedGestureScrollIndicatorState
import androidx.wear.compose.material3.onehandedgesture.oneHandedGesture
import androidx.wear.compose.material3.onehandedgesture.rememberOneHandedGestureConfiguration

val backDispatcherOwner = LocalOnBackPressedDispatcherOwner.current
val onClick =
    remember<() -> Unit> { { backDispatcherOwner?.onBackPressedDispatcher?.onBackPressed() } }
val scrollState = rememberTransformingLazyColumnState()
val coroutineScope = rememberCoroutineScope()

val buttonInteractionSource = remember { MutableInteractionSource() }
val buttonGestureConfig =
    rememberOneHandedGestureConfiguration(
        action = GestureAction.Primary,
        priority = GesturePriority.Clickable,
    )
val buttonIndicatorState = remember { OneHandedGestureClickIndicatorState() }

val scrollGestureConfig =
    rememberOneHandedGestureConfiguration(
        action = GestureAction.Primary,
        priority = GesturePriority.Scrollable,
    )
val scrollIndicatorState =
    remember(scrollGestureConfig) { OneHandedGestureScrollIndicatorState() }

ScreenScaffold(
    scrollState = scrollState,
    edgeButton = {
        EdgeButton(
            onClick = onClick,
            interactionSource = buttonInteractionSource,
            modifier =
                if (scrollState.canScrollForward) {
                    Modifier
                } else {
                    // Apply the one-handed gesture modifier only when the container cannot
                    // scroll further, ensuring the EdgeButton is fully visible and interactive
                    Modifier.oneHandedGesture(
                        gestureConfiguration = buttonGestureConfig,
                        interactionSource = buttonInteractionSource,
                        onGestureLabel = "close",
                        onGestureAvailable = {
                            coroutineScope.launch { buttonIndicatorState.showIndicator() }
                        },
                        onGesture = onClick,
                    )
                } then
                    Modifier.scrollable(
                        state = scrollState,
                        orientation = Orientation.Vertical,
                        reverseDirection = true,
                        overscrollEffect = rememberOverscrollEffect(),
                    ),
        ) {
            OneHandedGestureClickIndicator(buttonGestureConfig, buttonIndicatorState) {
                Text("Close")
            }
        }
    },
    scrollIndicator = {
        OneHandedGestureScrollIndicator(
            gestureConfiguration = scrollGestureConfig,
            indicatorState = scrollIndicatorState,
            scrollState = scrollState,
            modifier = Modifier.align(Alignment.CenterEnd),
        )
    },
) { contentPadding ->
    TransformingLazyColumn(
        state = scrollState,
        contentPadding = contentPadding,
        modifier =
            Modifier.fillMaxSize()
                .oneHandedGesture(
                    gestureConfiguration = scrollGestureConfig,
                    onGestureLabel = "scroll",
                    onGestureAvailable = {
                        coroutineScope.launch { scrollIndicatorState.showIndicator() }
                    },
                    onGesture = { OneHandedGestureDefaults.scrollDown(scrollState) },
                ),
    ) {
        items(10) { Text("Item $it") }
    }
}
Parameters
gestureConfiguration: OneHandedGestureConfiguration

the specification for the one-handed gesture

indicatorState: OneHandedGestureScrollIndicatorState

The state object used to synchronize the indicator visibility.

scrollState: TransformingLazyColumnState

The state object of the androidx.wear.compose.foundation.lazy.TransformingLazyColumn this indicator is coupled with.

modifier: Modifier = Modifier

The Modifier to be applied to the scroll indicator.

scrollIndicatorColors: ScrollIndicatorColors = ScrollIndicatorDefaults.colors()

ScrollIndicatorColors that will be used to resolve the indicator and track colors for this androidx.wear.compose.material3.ScrollIndicator.

gestureIndicatorTint: Color = OneHandedGestureDefaults.scrollIndicatorTint

The color which will be used for a tint of the gesture animation icon.

gestureIndicatorBackgroundColor: Color = OneHandedGestureDefaults.scrollIndicatorBackgroundColor

The color which will be used for a background behind the gesture animation.

reverseDirection: Boolean = false

Reverses direction of ScrollIndicator if true.

positionAnimationSpec: AnimationSpec<Float> = ScrollIndicatorDefaults.PositionAnimationSpec

AnimationSpec for position animation. The Position animation is used for animating changes to the scroll size and position. To disable this animation androidx.compose.animation.core.snap AnimationSpec should be passed instead.

See also the UI Design Guides for One-Handed Gestures