// ChainSegments — THE single-select segment control (usertest // finding #14 / night-log decision "pill segment as canon"): // the same choice pattern used to appear as four different // widgets (audit's hover pills, store's Material SegmentedButton, // store-filter ChoiceChips, approvals' TabBar). One look now: // text pills, primary-tinted when selected, quiet hover — the // audit page's pattern promoted to a shared widget. // // Single-select only by design. Multi-select filters are a // different pattern (checkbox list), not a segment control. import 'package:flutter/material.dart'; import '../theme/tokens.dart'; class ChainSegmentItem { final T value; final String label; final IconData? icon; const ChainSegmentItem(this.value, this.label, {this.icon}); } class ChainSegments extends StatelessWidget { final List> items; final T value; final ValueChanged onChanged; const ChainSegments({ super.key, required this.items, required this.value, required this.onChanged, }); @override Widget build(BuildContext context) { return Wrap( spacing: ChainSpace.xs, runSpacing: ChainSpace.xs, children: [ for (final item in items) _SegmentPill( label: item.label, icon: item.icon, selected: item.value == value, onTap: () { if (item.value != value) onChanged(item.value); }, ), ], ); } } class _SegmentPill extends StatefulWidget { final String label; final IconData? icon; final bool selected; final VoidCallback onTap; const _SegmentPill({ required this.label, required this.icon, required this.selected, required this.onTap, }); @override State<_SegmentPill> createState() => _SegmentPillState(); } class _SegmentPillState extends State<_SegmentPill> { bool _hovered = false; @override Widget build(BuildContext context) { final theme = Theme.of(context); final fg = widget.selected ? theme.colorScheme.primary : _hovered ? theme.colorScheme.onSurface : theme.colorScheme.onSurfaceVariant; final bg = widget.selected ? theme.colorScheme.primary.withValues(alpha: 0.12) : _hovered ? theme.colorScheme.surfaceContainerHigh : Colors.transparent; return Semantics( button: true, selected: widget.selected, label: widget.label, child: MouseRegion( onEnter: (_) => setState(() => _hovered = true), onExit: (_) => setState(() => _hovered = false), cursor: SystemMouseCursors.click, child: GestureDetector( onTap: widget.onTap, child: AnimatedContainer( duration: ChainMotion.fast, padding: const EdgeInsets.symmetric( horizontal: ChainSpace.md, vertical: 6, ), decoration: BoxDecoration( color: bg, borderRadius: BorderRadius.circular(ChainRadius.sm), border: Border.all( color: widget.selected ? theme.colorScheme.primary.withValues(alpha: 0.3) : Colors.transparent, ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ if (widget.icon != null) ...[ Icon(widget.icon, size: 14, color: fg), const SizedBox(width: ChainSpace.xs), ], Text( widget.label, style: theme.textTheme.labelMedium?.copyWith( fontWeight: widget.selected ? FontWeight.w600 : FontWeight.w400, color: fg, ), ), ], ), ), ), ), ); } }