haptic_rotary_scroll

A Wear OS Flutter package that provides a ScrollController with haptic feedback scaled to rotary scroll speed.

Unlike firing haptic feedback on every raw rotary event (which causes constant vibration during slow scrolling), this package accumulates rotation magnitude and fires a single HapticFeedback.selectionClick() per threshold crossed — so fast scrolling feels responsive and slow scrolling stays quiet.

Platform support

Android (Wear OS)

Depends on wearable_rotary for rotary event streaming.

Usage

Drop in as a replacement for ScrollController or RotaryScrollController:

class _MyScreenState extends State<MyScreen> {
  final _scrollController = HapticRotaryScrollController();

  @override
  void dispose() {
    _scrollController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      controller: _scrollController,
      child: ...,
    );
  }
}

Tuning haptic sensitivity

// Default — one click per 50px of rotation (one full-speed notch)
HapticRotaryScrollController()

// More sensitive — clicks more often
HapticRotaryScrollController(hapticPixelThreshold: 25)

// Less sensitive — clicks less often
HapticRotaryScrollController(hapticPixelThreshold: 100)

// Also cap the scroll distance per rotary event
HapticRotaryScrollController(maxIncrement: 80, hapticPixelThreshold: 40)

Parameters

Parameter Type Default Description
maxIncrement double 50 Max scroll pixels per rotary event (inherited from RotaryScrollController)
hapticPixelThreshold double maxIncrement Accumulated rotation pixels required to trigger one haptic click

How it works

Each RotaryEvent carries a magnitude representing how many pixels the crown/bezel rotated. This controller accumulates those magnitudes and fires haptic feedback once per hapticPixelThreshold pixels — meaning the click rate scales naturally with scroll speed. The accumulator resets on direction change so reversing direction always starts clean.

The subscription is always recreated on attach, which means it reconnects correctly after a scroll view is rebuilt (e.g. after search or navigation).