DEV Community

Cover image for Rotary Input in Wear OS with Compose
Vinícius Veríssimo
Vinícius Veríssimo

Posted on

Rotary Input in Wear OS with Compose

The Rotary Input is one more way to the user interact with their wearables devices. You must handle this type of input since your users will expect this even unconsciously.

Currently the users can perform the Rotary Input on smartwatches in 3 ways: crown, physical bezel, touch bezel. All of these option are transparent for us when handling the input, that is, for a simple handle of this input we don't need to know which one the user is using to interact with our app.

So, let's see some examples of how to handle this with ScalingLazyColumn and Picker.

💡 The following examples are using the version 1.1.0-rc01 of Composer for Wear OS

ScalingLazyColumn

First of all, in order the handle the Rotary input, the component must have the focus. We can achieve this by manually requesting the focus to a component. So, let's create a instance of the FocusRequester:

val focusRequester = remember { FocusRequester() }
Enter fullscreen mode Exit fullscreen mode

And then, set up this in our ScalingLazyColumn :

ScalingLazyColumn(
    modifier = Modifier
        .focusRequester(focusRequester)
        .focusable(),
    {...}
){
    {...}
}
Enter fullscreen mode Exit fullscreen mode

Next, we need to force the request of the focus.

LaunchedEffect(Unit){
    focusRequester.requestFocus() 
}
Enter fullscreen mode Exit fullscreen mode

Now our ScalingLazyColumn will always have the focus. The handle is not done yet, now we need to actually react to the Rotary Input and scroll our list with it.

First we need a instance of the ScalingLazyListState and use the modifier onRotaryScrollEvent to listen to the Rotary Inputs. Be aware that we need to perform this scroll with a Coroutine Scope.

val focusRequester = remember **{** FocusRequester() **}**
val scrollState = rememberScalingLazyListState()
val coroutineScope = rememberCoroutineScope()

ScalingLazyColumn(
    modifier = Modifier
                .onRotaryScrollEvent {
            coroutineScope.launch {
                scrollState.scrollBy(it.verticalScrollPixels)
            }
            true // it means that we are handling the event with this callback
        }
        .focusRequester(focusRequester)
        .focusable(),
        state = scrollState,
    {...}
){
    {...}
}

LaunchedEffect(Unit){
    focusRequester.requestFocus() 
}
Enter fullscreen mode Exit fullscreen mode

That's all. With a small configuration the ScalingLazyColumn is reacting to the Rotary Input.

Picker

For the Picker component the setup will be basically the same:

val pickerState = rememberPickerState(
    initialNumberOfOptions = 100,
    initiallySelectedOption = 50
)
val focusRequester = remember { FocusRequester() }
val coroutineScope = rememberCoroutineScope()

Picker(
    state = pickerState,
    contentDescription = pickerState.selectedOption.toString(),
    modifier = Modifier
        .onRotaryScrollEvent{
                    coroutineScope.launch{
                        pickerState.scrollBy(it.verticalScrollPixels)
                    }
                    true
                }
                .focusRequester(focusRequester)
          .focusable()
        {...}
    ) {
        {...}
    }

    LaunchedEffect(Unit) {
        focusRequester.requestFocus()
    }
Enter fullscreen mode Exit fullscreen mode

Conclusion

You notice some things:

  • There is no haptic feedback
    • If you test other apps that handle this input you will notice a haptic feedback during the scrolling, sadly this is not automatic, you need to do this manually or use the Horologist.
  • The Picker is not snapping to the selected item
    • With this approach sometimes the Picker doesn't snaps to the selected item, you can improve this with Horologist, but by my test it isn't working 100%.

The Rotary Input callbacks from the foundation library and Horologist are still in Experimental state, so soon they can change and be improved.

References

Code

You can check these and more example on my repository

GitHub logo Vnicius / rotary-input-samples

Sample codes showing the usage of Rotary input on Wear OS with Compose ⌚︎

Rotary Input Samples

Sample codes showing the usage of Rotary input on Wear OS with Compose ⌚︎

Samples

  • Basic Example - This sample contains the basic handle of Rotart input with ScalingLazyColumn and Picker.

Top comments (1)

Collapse
 
marlonlom profile image
Marlon López

How to perform the rotary input inside an android ui test?