androidx.wear.compose.foundation.rotary


Interfaces

RotaryScrollableBehavior

An interface for handling scroll events.

RotarySnapLayoutInfoProvider

A provider which connects scrollableState to a rotary input for snapping scroll actions.

Objects

RotaryScrollableDefaults

Defaults for rotaryScrollable modifier

Extension functions summary

Modifier
Modifier.rotaryScrollable(
    behavior: RotaryScrollableBehavior,
    focusRequester: FocusRequester,
    reverseDirection: Boolean
)

A modifier which connects rotary events with scrollable containers such as Column, LazyList and others.

Extension functions

fun Modifier.rotaryScrollable(
    behavior: RotaryScrollableBehavior,
    focusRequester: FocusRequester,
    reverseDirection: Boolean = false
): Modifier

A modifier which connects rotary events with scrollable containers such as Column, LazyList and others. ScalingLazyColumn has a build-in rotary support, and accepts RotaryScrollableBehavior directly as a parameter.

This modifier handles rotary input devices, used for scrolling. These devices can be categorized as high-resolution or low-resolution based on their precision.

  • High-res devices: Offer finer control and can detect smaller rotations. This allows for more precise adjustments during scrolling. One example of a high-res device is the crown (also known as rotating side button), located on the side of the watch.

  • Low-res devices: Have less granular control, registering larger rotations at a time. Scrolling behavior is adapted to compensate for these larger jumps. Examples include physical or virtual bezels, positioned around the screen.

This modifier supports rotary scrolling and snapping. The behaviour is configured by the provided RotaryScrollableBehavior: either provide RotaryScrollableDefaults.behavior for scrolling with/without fling or pass RotaryScrollableDefaults.snapBehavior when snap is required.

Example of scrolling with fling:

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.wear.compose.foundation.rememberActiveFocusRequester
import androidx.wear.compose.foundation.rotary.RotaryScrollableDefaults
import androidx.wear.compose.foundation.rotary.rotaryScrollable

val scrollableState = rememberLazyListState()
val focusRequester = rememberActiveFocusRequester()
LazyColumn(
    modifier = Modifier
        .fillMaxSize()
        .rotaryScrollable(
            behavior = RotaryScrollableDefaults.behavior(scrollableState),
            focusRequester = focusRequester
        ),
    horizontalAlignment = Alignment.CenterHorizontally,
    state = scrollableState
) {
    items(300) {
        BasicText(
            text = "item $it",
            modifier = Modifier.background(Color.Gray),
            style = TextStyle.Default.copy()
        )
    }
}

Example of scrolling with snap:

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastSumBy
import androidx.wear.compose.foundation.rememberActiveFocusRequester
import androidx.wear.compose.foundation.rotary.RotaryScrollableDefaults
import androidx.wear.compose.foundation.rotary.RotarySnapLayoutInfoProvider
import androidx.wear.compose.foundation.rotary.rotaryScrollable

val scrollableState = rememberLazyListState()
val focusRequester = rememberActiveFocusRequester()
LazyColumn(
    modifier = Modifier
        .fillMaxSize()
        .rotaryScrollable(
            behavior = RotaryScrollableDefaults.snapBehavior(
                scrollableState,
                // This sample has a custom implementation of RotarySnapLayoutInfoProvider
                // which is required for snapping behavior. ScalingLazyColumn has it built-in,
                // so it's not required there.
                remember(scrollableState) {
                    object : RotarySnapLayoutInfoProvider {

                        override val averageItemSize: Float
                            get() {
                            val items = scrollableState.layoutInfo.visibleItemsInfo
                            return (items.fastSumBy { it.size } / items.size).toFloat()
                        }

                        override val currentItemIndex: Int
                            get() =
                            scrollableState.firstVisibleItemIndex

                        override val currentItemOffset: Float
                            get() =
                            scrollableState.firstVisibleItemScrollOffset.toFloat()

                        override val totalItemCount: Int
                            get() =
                            scrollableState.layoutInfo.totalItemsCount
                    }
                }
            ),
            focusRequester = focusRequester
        ),
    horizontalAlignment = Alignment.CenterHorizontally,
    state = scrollableState
) {
    items(300) {
        BasicText(
            text = "item $it",
            modifier = Modifier
                .background(Color.Gray)
                .height(30.dp)
        )
    }
}
Parameters
behavior: RotaryScrollableBehavior

Specified RotaryScrollableBehavior for rotary handling with snap or fling.

focusRequester: FocusRequester

Used to request the focus for rotary input. Each composable with this modifier should have a separate focusRequester, and only one of them at a time can be active. We recommend using rememberActiveFocusRequester to obtain a FocusRequester, as this will guarantee the proper behavior.

reverseDirection: Boolean = false

Reverse the direction of scrolling if required for consistency with the scrollable state passed via behavior.