Files
pyramid/lib/widgets/hover_region.dart
T
Bernd Steckmeister 25ed765a03 wip: Sicherungs-Commit aller Änderungen seit April + Arbeitsstruktur (CLAUDE.md, ROADMAP.md, PROGRESS.md, Autopilot)
6 Wochen uncommittete Arbeit (Voice-Channels, LiveKit-Manager, Settings-Modal u.v.m.)
als ein WIP-Commit gesichert, damit nichts verloren geht und der Pi den aktuellen
Stand klonen kann. Thematische Aufarbeitung: siehe ROADMAP M0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CPrAGBxBT6GfPXzeWQ4AXb
2026-07-03 05:47:18 +02:00

118 lines
3.3 KiB
Dart

import 'package:flutter/material.dart';
// Simple hover-aware widget for desktop hover effects
class HoverRegion extends StatefulWidget {
final Widget Function(BuildContext context, bool hovered) builder;
final MouseCursor cursor;
const HoverRegion({
super.key,
required this.builder,
this.cursor = SystemMouseCursors.click,
});
@override
State<HoverRegion> createState() => _HoverRegionState();
}
class _HoverRegionState extends State<HoverRegion> {
bool _hovered = false;
@override
Widget build(BuildContext context) {
return MouseRegion(
cursor: widget.cursor,
onEnter: (_) => setState(() => _hovered = true),
onExit: (_) => setState(() => _hovered = false),
child: widget.builder(context, _hovered),
);
}
}
// Icon button styled to match Pyramid's .icon-btn
class PyrIconBtn extends StatefulWidget {
final Widget icon;
final VoidCallback? onPressed;
final String? tooltip;
final bool active;
final Color? activeColor;
final Color? dangerColor;
final double size;
const PyrIconBtn({
super.key,
required this.icon,
this.onPressed,
this.tooltip,
this.active = false,
this.activeColor,
this.dangerColor,
this.size = 32,
});
@override
State<PyrIconBtn> createState() => _PyrIconBtnState();
}
class _PyrIconBtnState extends State<PyrIconBtn> {
bool _hovered = false;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final isDark = theme.brightness == Brightness.dark;
final fgMuted = isDark ? const Color(0xFFA4A4B0) : const Color(0xFF54545E);
final fg = isDark ? const Color(0xFFECECF0) : const Color(0xFF1A1A1F);
final bgHover = isDark ? const Color(0xFF232330) : const Color(0xFFE9E7E1);
final color = widget.active
? (widget.activeColor ?? const Color(0xFFF5A614))
: _hovered ? fg : fgMuted;
final platform = Theme.of(context).platform;
final isMobile = platform == TargetPlatform.android || platform == TargetPlatform.iOS;
// On mobile, use a slightly larger touch target, but respect the requested size
final touchSize = isMobile ? (widget.size + 8).clamp(32.0, 48.0) : widget.size;
final btn = MouseRegion(
cursor: SystemMouseCursors.click,
onEnter: (_) => setState(() => _hovered = true),
onExit: (_) => setState(() => _hovered = false),
child: GestureDetector(
onTap: widget.onPressed,
child: SizedBox(
width: touchSize,
height: touchSize,
child: Center(
child: AnimatedContainer(
duration: const Duration(milliseconds: 150),
curve: Curves.easeOutCubic,
width: widget.size,
height: widget.size,
decoration: BoxDecoration(
color: _hovered ? bgHover : Colors.transparent,
borderRadius: BorderRadius.circular(7),
),
child: Center(
child: IconTheme(
data: IconThemeData(color: color, size: 16),
child: widget.icon,
),
),
),
),
),
),
);
if (widget.tooltip != null) {
return Tooltip(
message: widget.tooltip!,
waitDuration: const Duration(milliseconds: 500),
child: btn,
);
}
return btn;
}
}