wear_os_scrollbar 1.0.0
wear_os_scrollbar: ^1.0.0 copied to clipboard
A Flutter plugin to show Material 3 Expressive scroll indicator for WearOS with customizable haptic response.
WearOS Scrollbar #
A Flutter plugin that implements an elegant, expressive scroll indicator for Wear OS devices, following the Material 3 Expressive design guidelines for Wear OS 6.
This package provides a customizable circular scrollbar that automatically responds to physical rotary input (such as a rotating bezel or crown) and provides haptic feedback, making your Wear OS applications feel truly native and premium.
β¨ Features #
- Material 3 Expressive Design: Modern, sleek circular scrollbar tailored specifically for round screens, with support for the dynamic fisheye scaling effect via
WearOsExpressiveItem. - Smooth Rotary Input & Natural Decay: Automatically listens to the watch's physical rotary encoder (crown/bezel) and scrolls with continuous exponential decay physics matching Wear OS system settings.
- Native Wear OS Rotary Haptics: Official Android
ROTARY_SCROLL_TICKcrown detents andROTARY_SCROLL_LIMITboundary clicks for Pixel Watch 3 and modern Wear OS devices. - Touch Interruption: Fluidly cancels active rotary deceleration whenever the user touches the screen to perform a manual drag.
- Highly Customizable: Easily configure scroll velocity (
rotarySensitivity), smooth scrolling, colors, stroke width, margins, and span angle. - Hide Indicator: Option to hide the visual indicator while keeping rotary and haptic functionality.
- Auto-hiding: Smoothly fades out when not actively scrolling.
πΈ Example App #
![]() |
![]() |
![]() |
|---|---|---|
| Initial View | Scrolling Down | Reaching Bottom |
π Installation #
Add the dependency to your pubspec.yaml:
dependencies:
wear_os_scrollbar: ^1.0.0
βοΈ Configuration #
Ensure your android/app/build.gradle is configured correctly for Wear OS:
android {
defaultConfig {
minSdkVersion 26 // Or your current minSdk, ideally 26+ for modern Android/WearOS features
}
}
To make the app assume the rounded canvas appearance on rounded screens, you can add this to your MainActivity.kt:
import android.os.Bundle
import io.flutter.embedding.android.FlutterActivity
class MainActivity : FlutterActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
intent.putExtra("background_mode", "transparent")
super.onCreate(savedInstanceState)
}
}
π Usage #
Wrap your scrollable content (like a ListView or SingleChildScrollView) with the WearOsScrollbar widget. You can also wrap individual items in WearOsExpressiveItem for Material 3 Expressive scaling.
Important: You must provide the exact same ScrollController to both the WearOsScrollbar and your scrollable child.
import 'package:material_ui/material_ui.dart';
import 'package:wear_os_scrollbar/wear_os_scrollbar.dart';
class MyWearOsScreen extends StatefulWidget {
const MyWearOsScreen({super.key});
@override
State<MyWearOsScreen> createState() => _MyWearOsScreenState();
}
class _MyWearOsScreenState extends State<MyWearOsScreen> {
final ScrollController _controller = ScrollController();
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: WearOsScrollbar(
controller: _controller,
// Optional customizations (default values calibrated for native Wear OS):
hapticFeedback: WearOsHapticFeedback.rotaryTick,
rotarySensitivity: 0.4,
enableSmoothScroll: true,
enableLimitHaptic: true,
child: ListView.builder(
controller: _controller, // MUST be the same controller
itemCount: 50,
itemBuilder: (context, index) {
return WearOsExpressiveItem(
scrollController: _controller,
child: ListTile(
title: Text('Item $index'),
),
);
},
),
),
);
}
}
π± Using with PageView #
WearOsScrollbar also works perfectly with PageView. If you want to use the rotary input to navigate between pages but prefer to show your own custom page indicator (like dots or a curved indicator), you can set hideIndicator: true.
WearOsScrollbar(
controller: _pageController,
hideIndicator: true, // Hides the default scrollbar but keeps rotary input working
child: PageView(
controller: _pageController,
scrollDirection: Axis.vertical, // Works best for vertical PageView on Wear OS
children: [
PageOne(),
PageTwo(),
PageThree(),
],
),
)
WearOsScrollbar Options #
| Parameter | Type | Default | Description |
|---|---|---|---|
controller |
ScrollController |
Required | The controller attached to the scrollable widget inside. |
child |
Widget |
Required | The scrollable content (e.g., ListView). |
hapticScrollThreshold |
double |
24.0 |
How much rotary scrolling must accumulate before triggering a haptic click. |
hapticFeedback |
WearOsHapticFeedback |
.rotaryTick |
The type of haptic feedback (rotaryTick, vibrate, lightImpact, mediumImpact, heavyImpact, selectionClick, none). |
enableLimitHaptic |
bool |
true |
Whether to trigger tactile limit feedback (ROTARY_SCROLL_LIMIT) when hitting the top or bottom of the list. |
rotarySensitivity |
double |
0.4 |
Sensitivity multiplier for rotary encoder events (calibrated to match native Wear OS / Pixel Watch settings). |
enableSmoothScroll |
bool |
true |
Whether to interpolate rotary scrolling with natural decay physics instead of jumping abruptly. |
indicatorColor |
Color |
Colors.white |
Color of the active scroll indicator. |
backgroundColor |
Color |
Colors.white30 |
Color of the background track arc. |
strokeWidth |
double |
6.0 |
Thickness of the scrollbar (must be between 1 and 10). |
marginRight |
double |
0.0 |
Distance from the physical edge of the screen (must be between 0 and 50). |
totalAngle |
double |
30.0 |
Total span angle of the scrollbar area (must be between 10 and 90 degrees). |
hideIndicator |
bool |
false |
Whether to hide the visual scroll indicator while maintaining rotary and haptic support. |
WearOsExpressiveItem Options #
| Parameter | Type | Default | Description |
|---|---|---|---|
scrollController |
ScrollController |
Required | The controller of the parent scrollable widget. |
child |
Widget |
Required | The child widget to be dynamically scaled. |
minScale |
double |
0.5 |
Minimum scale factor when the item reaches the top or bottom edges of the viewport. |
maxScale |
double |
1.0 |
Maximum scale factor when the item is within the central area of the viewport. |
π License #
Distributed under the MIT License. See LICENSE for more information.


