import 'package:flutter/widgets.dart'; import 'package:pyramid/core/theme.dart'; /// Federnde Druck-Rückmeldung: beim Drücken leicht kleiner, beim Loslassen /// elastisch zurück (Bernds Wunsch 2026-10-07: mehr „Springiness“). /// /// Hört nur passiv mit (Listener) – Tippen, Halten und Scrollen bleiben beim /// Kind bzw. beim umgebenden Scrollbereich. Bewegt sich der Finger (Scrollen), /// wird die Rückmeldung sofort abgebrochen. Mit „Animationen: 0 %“ in den /// Einstellungen (PyramidTheme.motion) ist sie aus. class SpringPress extends StatefulWidget { const SpringPress({ super.key, required this.child, this.pressedScale = 0.88, this.enabled = true, }); final Widget child; final double pressedScale; final bool enabled; @override State createState() => _SpringPressState(); } class _SpringPressState extends State { static const _moveSlop = 8.0; bool _pressed = false; Offset? _down; void _set(bool pressed) { if (_pressed != pressed && mounted) setState(() => _pressed = pressed); } @override Widget build(BuildContext context) { final motion = PyramidTheme.of(context).motion; if (!widget.enabled || motion < 0.05) return widget.child; return Listener( behavior: HitTestBehavior.translucent, onPointerDown: (e) { _down = e.position; _set(true); }, onPointerMove: (e) { final d = _down; if (d != null && (e.position - d).distance > _moveSlop) _set(false); }, onPointerUp: (_) => _set(false), onPointerCancel: (_) => _set(false), child: AnimatedScale( scale: _pressed ? widget.pressedScale : 1.0, duration: _pressed ? const Duration(milliseconds: 90) : Duration(milliseconds: (420 * motion).round().clamp(120, 420)), curve: _pressed ? Curves.easeOut : Curves.elasticOut, child: widget.child, ), ); } } /// Gemeinsame „federnde“ Kurve für Ein-/Ausblendungen (leichtes Überschwingen). const springyCurve = Cubic(0.18, 0.89, 0.32, 1.12); /// Dauer, skaliert mit der Einstellung „Animationen“ (0 = sofort). Duration motionDuration(BuildContext context, int milliseconds) { final motion = PyramidTheme.of(context).motion; return Duration(milliseconds: (milliseconds * motion).round()); }