feat: Handy - groessere Symbole, Suche/Mitglieder im Menue, Kontakt-Banner repariert, Schrift 110 %, federnde Knoepfe
Bernds Feedback 2026-10-07: - Schrift-Standard Handy 110 % (statt 115 %). - PyrIconBtn: im Handy-Layout Symbol 22 px (vorher fest 16), Flaeche 40, Tippflaeche 44; Farben aus dem Farbmodus statt fest verdrahtet. - Eingabeleiste: Anhaengen/GIF/Emoji/Senden auf dem Handy 22-23 px statt 12-13 px. - Chat-Kopf (Handy): Anrufen + Videoanruf sichtbar, Suchen + Mitglieder im Drei-Punkte-Menue. - Aufklappbarer DM-Kopf als Ebenen: Zurueck + Symbole bleiben oben, Profilbereich gleitet darunter und nutzt die volle Breite (vorher Name zu "p..." gekuerzt, Symbole rutschten in die Mitte); Kuerzel im Avatar mittig, skaliert mit. - SpringPress (widgets/spring_press.dart): federnde Druck-Rueckmeldung, aus bei "Animationen 0 %". Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
36875ef9cd
commit
382747cdb1
@@ -0,0 +1,71 @@
|
||||
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<SpringPress> createState() => _SpringPressState();
|
||||
}
|
||||
|
||||
class _SpringPressState extends State<SpringPress> {
|
||||
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());
|
||||
}
|
||||
Reference in New Issue
Block a user