import "package:flutter/widgets.dart"; import "package:garage_ui/theme/garage_theme.dart"; // A chrome panel - the bordered, rounded surface the shell docks things in. // Fills with the scheme's panel colour and lights its border with the // highlighted colour when active. // // Two modes: // - standalone: leave [active] null and it tracks its own hover. // - controlled: pass [active] and the parent drives the lit state (used by the // shell so it can keep the panels mutually exclusive - only one lit at once, // which also dodges flutter dropping an onExit during a fast mouse move and // leaving a panel stuck on). class Panel extends StatefulWidget { const Panel({ super.key, required this.child, this.active, this.borderRadius, this.borderWidth = 1.15, this.clip = true, this.clipBehavior = Clip.antiAlias, }); final Widget child; // null = self-track hover; non-null = parent controls the lit state. final bool? active; // null = use the theme's panelRadius. final double? borderRadius; final double borderWidth; // clip the child to the rounded corners. final bool clip; final Clip clipBehavior; @override State createState() => _PanelState(); } class _PanelState extends State { bool _hovered = false; @override Widget build(BuildContext context) { final theme = GarageTheme.of(context); final cs = theme.colorScheme; final controlled = widget.active != null; final lit = controlled ? widget.active! : _hovered; final radius = BorderRadius.circular( widget.borderRadius ?? theme.panelRadius, ); Widget content = widget.child; if (widget.clip) { final clipRadius = BorderRadius.circular( ((widget.borderRadius ?? theme.panelRadius) - 1) .clamp(0, double.infinity) .toDouble(), ); content = ClipRRect( borderRadius: clipRadius, clipBehavior: widget.clipBehavior, child: content, ); } Widget box = Container( decoration: BoxDecoration( color: cs.background, borderRadius: radius, border: Border.all( color: lit ? cs.panelBorderHighlighted : cs.panelBorder, width: widget.borderWidth, ), ), child: content, ); // controlled panels let the parent own the MouseRegion. if (controlled) return box; return MouseRegion( onEnter: (_) => setState(() => _hovered = true), onExit: (_) => setState(() => _hovered = false), child: box, ); } }