gesture_physics_animator 0.1.1 copy "gesture_physics_animator: ^0.1.1" to clipboard
gesture_physics_animator: ^0.1.1 copied to clipboard

Velocity-seeded spring physics for draggable widgets: real momentum, elastic boundary bouncing, and configurable damping — not a tween-based "snap back."

gesture_physics_animator 🌀 #

Turn any widget into a spring-backed, draggable, elastic element — driven by real physics, not a tween that fakes it.

Free drag, bounded, and snap-to-dock demos

📱 Platforms supported #

Pure Dart/Flutter — no platform channels, no native code — so it runs anywhere Flutter does:

Android iOS Web Windows macOS Linux

✨ Why not just Tween + Curves.elasticOut? 🤔 #

Most "drag and snap back" widgets animate release with a Tween and a canned curve. That looks fine for a slow drag, but a fast flick feels disconnected — the curve has no idea how fast you were actually moving, so the snap-back always starts from a standstill.

GesturePhysicsAnimator seeds a real SpringSimulation (or, when not snapping back, a FrictionSimulation) with the actual release velocity from your drag, driven through a raw AnimationController via animateWith() — never a Tween. The result carries your throw's momentum straight into the settle animation: overshoot-and-settle, elastic boundary bouncing, momentum-based throws, all continuous with how you actually moved your finger. 🎯

🚀 Features #

  • 🧲 Velocity-seeded spring release — no jarring switch to a "fake" curve
  • 🧱 Elastic boundary bouncing with configurable energy loss per bounce
  • 🎯 Snap-to-target (dock icons, layout guides, anywhere you want)
  • 🔒 Optional single-axis locking
  • 🎛️ Named spring presets (gentle, snappy, bouncy) plus full control over raw SpringDescription values
  • 🪶 Physics math is fully isolated and unit-tested independent of widgets

📦 Installation #

dependencies:
  gesture_physics_animator: ^0.1.0

⚡ Quick start #

import 'package:gesture_physics_animator/gesture_physics_animator.dart';

GesturePhysicsAnimator(
  spring: SpringPresets.snappy,
  child: const FlutterLogo(size: 80),
)

That's it — drag it, flick it, watch it settle. Since it moves its child with a Transform (no extra layout space reserved), it's usually placed inside a Stack for free 2-D dragging:

Stack(
  children: [
    GesturePhysicsAnimator(
      spring: SpringPresets.bouncy,
      bounds: Rect.fromLTRB(-150, -150, 150, 150),
      boundaryDamping: 0.4,
      child: const FlutterLogo(size: 80),
    ),
  ],
)

🎛️ Physics parameters, in plain terms #

SpringDescription has three numbers. You don't need a physics degree to tune them:

Parameter Plain-English meaning
stiffness How hard it pulls back toward rest. Higher = snappier, faster to settle. Think "how stiff is the rubber band."
damping How much the motion is resisted. Higher = less bouncy, fewer (or zero) oscillations before it stops. Think "how much air resistance."
mass How heavy the object feels. Higher = slower to speed up and slower to slow down, for the same stiffness/damping.

Don't want to hand-tune these? Use a preset: 🧘 SpringPresets.gentle for a calm, controlled settle · ⚡ SpringPresets.snappy (the default) for a crisp, native-feeling response · 🏀 SpringPresets.bouncy for a playful, elastic bounce.

GesturePhysicsAnimator(
  spring: SpringPresets.gentle, // or .snappy, .bouncy, or your own SpringDescription(...)
  child: myWidget,
)

🧱 Bounded, elastic dragging #

Give it a bounds rectangle (relative to the widget's own starting position — Offset.zero is always inside it) and it bounces elastically off the edges instead of dragging straight through them:

GesturePhysicsAnimator(
  bounds: Rect.fromLTRB(-100, -100, 100, 100),
  boundaryDamping: 0.5, // 0 = perfectly elastic, 1 = stops dead at the edge
  child: myWidget,
)

🎯 Snapping to a target #

By default the widget always springs back to its origin on release (snapBackToOrigin: true). Point it at a custom rest position with snapTarget, or set snapBackToOrigin: false to let a throw keep going and settle wherever momentum carries it — useful for "leave it where you dropped it" widgets like a draggable FAB.

🧩 Presets #

  • DraggableCard — the common "drag it, let go, it springs back" card
  • ElasticBoundedDraggable — bounded + bouncy, for a draggable FAB or floating panel that shouldn't leave the screen
DraggableCard(child: myCard);

ElasticBoundedDraggable(
  bounds: Rect.fromLTRB(-200, -400, 200, 400),
  child: FloatingActionButton(onPressed: () {}, child: const Icon(Icons.add)),
);

🖼️ Example app #

The example/ app has three interactive demos:

  1. Free drag — sliders for stiffness and damping so you can feel the difference live
  2. Bounded — a card that bounces elastically off the screen edges
  3. Snap dock — drag and release snaps to the nearest of three dock zones

Run it with:

cd example
flutter run

🧪 Testing #

The physics core (GestureSpringController, BoundedAxisSimulation) is plain Dart with zero widget dependencies, so it's tested by driving it directly with TestVSync and a manually-pumped clock — no widget tree, no synthesized pointer events. Gesture-level behavior (drag, fling, bounds, axis lock, interrupted animations) is covered separately with real WidgetTester.fling()/drag() simulation. See test/.

📄 License #

MIT — see LICENSE.

0
likes
160
points
136
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Velocity-seeded spring physics for draggable widgets: real momentum, elastic boundary bouncing, and configurable damping — not a tween-based "snap back."

Repository (GitHub)
View/report issues

Topics

#animation #gestures #physics #drag #interactive

License

MIT (license)

Dependencies

flutter

More

Packages that depend on gesture_physics_animator