import 'package:flutter/material.dart'; import '../../core/theme/design_tokens.dart'; class DetailHeaderActionItem { const DetailHeaderActionItem({ required this.value, required this.label, required this.icon, this.isDestructive = false, this.enabled = true, }); final T value; final String label; final IconData icon; final bool isDestructive; final bool enabled; } class DetailHeaderActionMenu extends StatefulWidget { const DetailHeaderActionMenu({ super.key, required this.items, required this.onSelected, }); final List> items; final ValueChanged onSelected; @override State> createState() => _DetailHeaderActionMenuState(); } class _DetailHeaderActionMenuState extends State> { static const double _buttonSize = AppSpacing.xl * 2; static const double _menuWidth = AppSpacing.xxl * 8; final LayerLink _layerLink = LayerLink(); OverlayEntry? _menuEntry; bool get _isMenuOpen => _menuEntry != null; @override void dispose() { _removeOverlayEntry(); super.dispose(); } void _toggleMenu() { if (_isMenuOpen) { _hideMenu(); return; } _showMenu(); } void _showMenu() { final overlay = Overlay.of(context); _menuEntry = OverlayEntry( builder: (context) { return Stack( children: [ Positioned.fill( child: GestureDetector( behavior: HitTestBehavior.opaque, onTap: _hideMenu, child: const SizedBox.expand(), ), ), CompositedTransformFollower( link: _layerLink, showWhenUnlinked: false, offset: Offset( _buttonSize - _menuWidth, _buttonSize + AppSpacing.sm, ), child: _buildMenuCard(), ), ], ); }, ); overlay.insert(_menuEntry!); setState(() {}); } void _removeOverlayEntry() { _menuEntry?.remove(); _menuEntry = null; } void _hideMenu() { _removeOverlayEntry(); if (mounted) { setState(() {}); } } void _handleSelect(T value) { _hideMenu(); widget.onSelected(value); } Widget _buildMenuCard() { final colorScheme = Theme.of(context).colorScheme; return Material( color: colorScheme.surface.withValues(alpha: 0), child: Container( width: _menuWidth, padding: const EdgeInsets.symmetric(vertical: AppSpacing.sm), decoration: BoxDecoration( color: colorScheme.surface, borderRadius: BorderRadius.circular(AppRadius.lg), border: Border.all(color: colorScheme.outlineVariant), boxShadow: [ BoxShadow( color: colorScheme.shadow.withValues(alpha: 0.42), blurRadius: AppSpacing.xl, offset: const Offset(0, AppSpacing.sm), ), ], ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ for (int i = 0; i < widget.items.length; i++) ...[ _buildMenuItem(widget.items[i]), if (i < widget.items.length - 1) Padding( padding: const EdgeInsets.symmetric( horizontal: AppSpacing.md, ), child: Divider( height: 1, thickness: 1, color: colorScheme.outlineVariant, ), ), ], ], ), ), ); } Widget _buildMenuItem(DetailHeaderActionItem item) { final colorScheme = Theme.of(context).colorScheme; final textColor = item.isDestructive ? colorScheme.error : (item.enabled ? colorScheme.onSurface : colorScheme.onSurfaceVariant); final pressedColor = item.isDestructive ? colorScheme.errorContainer : colorScheme.primaryContainer; return SizedBox( height: AppSpacing.xxl * 2, child: Material( color: colorScheme.surface.withValues(alpha: 0), child: InkWell( borderRadius: BorderRadius.circular(AppRadius.md), splashColor: item.enabled ? pressedColor : colorScheme.surface.withValues(alpha: 0), highlightColor: item.enabled ? pressedColor : colorScheme.surface.withValues(alpha: 0), onTap: item.enabled ? () => _handleSelect(item.value) : null, child: Padding( padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Icon( item.icon, size: AppSpacing.lg + AppSpacing.xs, color: textColor, ), const SizedBox(width: AppSpacing.md), Text( item.label, style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, color: textColor, ), ), ], ), ), ), ), ); } @override Widget build(BuildContext context) { if (widget.items.isEmpty) { return const SizedBox.shrink(); } final colorScheme = Theme.of(context).colorScheme; return CompositedTransformTarget( link: _layerLink, child: GestureDetector( onTap: _toggleMenu, child: AnimatedContainer( duration: const Duration(milliseconds: 140), width: _buttonSize, height: _buttonSize, decoration: BoxDecoration( color: _isMenuOpen ? colorScheme.secondaryContainer : colorScheme.tertiaryContainer, borderRadius: BorderRadius.circular(AppRadius.md), border: Border.all( color: _isMenuOpen ? colorScheme.outlineVariant : colorScheme.outline, ), ), child: Icon( Icons.more_horiz, size: AppSpacing.lg + AppSpacing.xs, color: colorScheme.onSurfaceVariant, ), ), ), ); } }