scroll_velocity_notifier

scroll_velocity_notifier is a lightweight Flutter utility that intercepts scroll notifications and computes smooth, real-time scroll velocity (pixels per second).

It is designed for scroll-aware UI, gesture-driven effects, and advanced animations, without imposing layout constraints or architectural opinions.


โœจ Features

  • ๐Ÿ“ Calculates scroll velocity in pixels per second
  • ๐Ÿ“‰ Uses Exponential Moving Average (EMA) for smooth values
  • ๐ŸŒŠ Optional overscroll velocity support
  • ๐Ÿงฉ Implemented as a ProxyWidget (zero layout impact)
  • ๐Ÿ”Œ Works with any ScrollView
  • ๐Ÿง  No global state, no forced state management

๐Ÿ“ฆ Installation

Add the dependency to your pubspec.yaml:

dependencies:
  scroll_velocity_notifier: ^0.0.1

Then run:

flutter pub get

๐Ÿ“ธ Demo - gif is removes frames so it looks junky on the gif

Scroll velocity demo


๐Ÿง  How It Works

The widget listens to ScrollNotifications emitted by scrollable widgets and computes velocity using:

  • Scroll position delta (pixels)
  • Time delta (microseconds)
  • EMA smoothing for stability

The widget does not alter layout or scrolling behavior. It acts purely as a transparent observer in the widget tree.


๐Ÿš€ Basic Usage

Wrap any scrollable widget with ScrollVelocityNotifier:

ScrollVelocityNotifier(
  onNotification: (notification, velocity) {
    debugPrint('Velocity: $velocity px/s');
    return false; // allow notification to bubble up
  },
  child: ListView.builder(
    itemCount: 50,
    itemBuilder: (context, index) {
      return ListTile(
        title: Text('Item $index'),
      );
    },
  ),
)

๐Ÿ“ Velocity Semantics

  • Positive velocity โ†’ scrolling down
  • Negative velocity โ†’ scrolling up
  • Zero velocity โ†’ stationary or ignored overscroll
  • Smoothed output โ†’ ideal for UI reactions and animations

๐ŸŒŠ Overscroll Support

By default, velocity is reported as 0 during overscroll.

To include overscroll velocity (e.g. when using BouncingScrollPhysics):

ScrollVelocityNotifier(
  includeOversScroll: true,
  onNotification: (notification, velocity) {
    debugPrint('Overscroll velocity: $velocity');
    return false;
  },
  child: ListView(
    physics: const BouncingScrollPhysics(),
    children: const [
      SizedBox(height: 2000),
    ],
  ),
)

๐ŸŽฏ Use Case Examples

Hide / Show AppBar Based on Scroll Speed

double appBarOffset = 0;

ScrollVelocityNotifier(
  onNotification: (notification, velocity) {
    if (velocity > 800) {
      appBarOffset = -100;
    } else if (velocity < -800) {
      appBarOffset = 0;
    }
    return false;
  },
  child: CustomScrollView(
    slivers: [
      SliverAppBar(
        floating: true,
        expandedHeight: 100,
      ),
      SliverList(
        delegate: SliverChildBuilderDelegate(
          (context, index) => ListTile(title: Text('Item $index')),
          childCount: 50,
        ),
      ),
    ],
  ),
)

Trigger Animations Based on Scroll Velocity

ScrollVelocityNotifier(
  onNotification: (notification, velocity) {
    if (velocity.abs() > 1200) {
      debugPrint('Fast scroll detected');
    }
    return false;
  },
  child: ListView(
    children: List.generate(
      30,
      (i) => ListTile(title: Text('Row $i')),
    ),
  ),
)

๐Ÿ”Œ StreamController Integration

ScrollVelocityNotifier can optionally emit scroll velocity updates into a user-provided StreamController.

This allows scroll velocity data to be consumed outside the widget tree, for example by:

  • BLoC / Cubit
  • analytics systems
  • animation coordinators
  • logging or debugging tools

Basic Usage

final controller =
    StreamController<ScrollStreamNotification>.broadcast();

@override
void dispose() {
  controller.close();
  super.dispose();
}

ScrollVelocityNotifier(
  controller: controller,
  child: ListView.builder(
    itemCount: 50,
    itemBuilder: (context, index) {
      return ListTile(title: Text('Item $index'));
    },
  ),
);


---

## ๐Ÿง  Architectural Notes

* Implemented using `ProxyWidget` + `ProxyElement`
* No rebuilds are triggered
* No inherited state
* No frame callbacks
* Safe for high-frequency scroll updates

This makes it suitable for **large dashboards** and **complex scroll hierarchies**.


---

## ๐Ÿงช Testing

The velocity stream can be tested by driving scroll notifications and asserting expected velocity output:

```dart
expect(
  velocity.abs(),
  greaterThan(0),
);

๐Ÿ› ๏ธ When to Use This Package

โœ” Scroll-aware UI โœ” Velocity-driven animations โœ” Gesture-based visibility logic โœ” Overscroll-sensitive effects โœ” Performance-safe scroll observation


๐Ÿ“„ License

MIT License See LICENSE file for details.


๐Ÿ™Œ Contributions

Issues and pull requests are welcome. If you find a bug or have a feature idea, feel free to open an issue.


If you want next:

  • a CHANGELOG.md
  • an example/ app
  • pub.dev score optimization
  • API tightening (ownership-safe controller handling)

Just tell me.