Files
Garage-SDKs/garage_ui/lib/panel.dart
T
ImBenjiandClaude Opus 5.5 b269201919 The Garage SDKs, in the open
garage_auth, garage_entitlements, garage_iap and garage_ui, moved out of
Garage-Services and Metro-Map-Maker into one public repo. MIT, one readme,
docs under docs/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013F4NWNvYcdeSgqbWMT1VQ7
2026-09-23 18:49:21 +01:00

92 lines
2.5 KiB
Dart

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<Panel> createState() => _PanelState();
}
class _PanelState extends State<Panel> {
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,
);
}
}