import 'package:flutter/material.dart'; import 'package:lucide_icons/lucide_icons.dart'; import '../providers/voice_provider.dart'; /// Animated mic button that reflects the current [VoiceMode]. /// /// - idle: muted background, mic_none icon /// - recording: red, pulses with live [amplitude] for real-time feedback /// - transcribing: indigo with spinner /// - playing: indigo with volume_up icon class VoiceMicButton extends StatelessWidget { final VoiceMode mode; final bool voiceModeActive; /// Live mic amplitude 0.0–1.0 while recording. Drives the button scale /// and glow so the user has obvious feedback that audio is being picked /// up. Ignored when not in [VoiceMode.recording]. final double amplitude; final VoidCallback? onTap; const VoiceMicButton({ super.key, required this.mode, required this.voiceModeActive, this.amplitude = 0.0, this.onTap, }); Color _bgColor(BuildContext context) { final cs = Theme.of(context).colorScheme; return switch (mode) { VoiceMode.recording => const Color(0xFFEF4444), VoiceMode.transcribing || VoiceMode.playing => cs.primary, VoiceMode.idle => cs.surfaceContainerHighest, }; } Widget _icon(BuildContext context) { final cs = Theme.of(context).colorScheme; final iconColor = mode == VoiceMode.idle ? cs.onSurfaceVariant : Colors.white; return switch (mode) { VoiceMode.transcribing => SizedBox( width: 18, height: 18, child: CircularProgressIndicator( strokeWidth: 2, color: iconColor, ), ), VoiceMode.playing => Icon(LucideIcons.volume2, color: iconColor, size: 20), _ => Icon(LucideIcons.mic, color: iconColor, size: 20), }; } @override Widget build(BuildContext context) { final isRecording = mode == VoiceMode.recording; final button = Material( color: _bgColor(context), shape: const CircleBorder(), child: InkWell( customBorder: const CircleBorder(), onTap: onTap, child: SizedBox( width: 40, height: 40, child: Center(child: _icon(context)), ), ), ); if (!isRecording) return button; // Base pulse so silence still breathes (0.1 floor), scale + glow climb // linearly with live amplitude. final amp = amplitude.clamp(0.0, 1.0); final pulse = 0.1 + amp * 0.9; return AnimatedContainer( duration: const Duration(milliseconds: 120), curve: Curves.easeOut, decoration: BoxDecoration( shape: BoxShape.circle, boxShadow: [ BoxShadow( color: const Color(0xFFEF4444).withValues(alpha: 0.2 + pulse * 0.3), blurRadius: 6 + pulse * 14, spreadRadius: 1 + pulse * 5, ), ], ), child: AnimatedScale( scale: 1.0 + pulse * 0.18, duration: const Duration(milliseconds: 120), curve: Curves.easeOut, child: button, ), ); } }