import 'dart:async'; import 'package:flutter/material.dart'; import 'package:vibration/vibration.dart'; import '../theme/app_theme.dart'; /// Large circular talk button. Tap to toggle recording. /// Long-press while recording (600ms) cancels. class VoiceButton extends StatefulWidget { final bool isRecording; final VoidCallback onTapStart; final VoidCallback onTapStop; final VoidCallback onCancel; const VoiceButton({ super.key, required this.isRecording, required this.onTapStart, required this.onTapStop, required this.onCancel, }); @override State createState() => _VoiceButtonState(); } class _VoiceButtonState extends State with SingleTickerProviderStateMixin { late final AnimationController _pulseController; Timer? _longPressTimer; @override void initState() { super.initState(); _pulseController = AnimationController( vsync: this, duration: const Duration(milliseconds: 1500), ); } @override void didUpdateWidget(VoiceButton oldWidget) { super.didUpdateWidget(oldWidget); if (widget.isRecording && !oldWidget.isRecording) { _pulseController.repeat(reverse: true); } else if (!widget.isRecording && oldWidget.isRecording) { _pulseController.stop(); _pulseController.value = 0; } } @override void dispose() { _pulseController.dispose(); _longPressTimer?.cancel(); super.dispose(); } void _onTap() { if (widget.isRecording) { widget.onTapStop(); } else { widget.onTapStart(); } _haptic(); } void _onLongPressStart(LongPressStartDetails _) { if (widget.isRecording) { _longPressTimer = Timer(const Duration(milliseconds: 600), () { widget.onCancel(); _haptic(); }); } } void _onLongPressEnd(LongPressEndDetails _) { _longPressTimer?.cancel(); } Future _haptic() async { try { final hasVibrator = await Vibration.hasVibrator(); if (hasVibrator) { Vibration.vibrate(duration: 20); } } catch (_) {} } @override Widget build(BuildContext context) { return GestureDetector( onTap: _onTap, onLongPressStart: _onLongPressStart, onLongPressEnd: _onLongPressEnd, child: AnimatedBuilder( animation: _pulseController, builder: (context, child) { final glowRadius = widget.isRecording ? 8.0 + (_pulseController.value * 12.0) : 0.0; return Container( width: 72, height: 72, decoration: BoxDecoration( color: widget.isRecording ? AppColors.recording : AppColors.accent, shape: BoxShape.circle, boxShadow: widget.isRecording ? [ BoxShadow( color: AppColors.recordingGlow, blurRadius: glowRadius, spreadRadius: glowRadius / 2, ), ] : [ BoxShadow( color: AppColors.accent.withAlpha(60), blurRadius: 8, spreadRadius: 2, ), ], ), child: Icon( widget.isRecording ? Icons.stop : Icons.mic, color: Colors.white, size: 32, ), ); }, ), ); } }