import 'package:flutter/material.dart'; import 'package:lba/res/colors.dart'; import 'package:audioplayers/audioplayers.dart'; import 'dart:async'; class RecordedAudioPreview extends StatefulWidget { final String audioPath; final Duration totalDuration; final VoidCallback onDelete; final String Function(Duration) formatDuration; const RecordedAudioPreview({ super.key, required this.audioPath, required this.totalDuration, required this.onDelete, required this.formatDuration, }); @override _RecordedAudioPreviewState createState() => _RecordedAudioPreviewState(); } class _RecordedAudioPreviewState extends State { final AudioPlayer _audioPlayer = AudioPlayer(); bool _isPlaying = false; Duration _playDuration = Duration.zero; late Duration _totalDuration; StreamSubscription? _positionSubscription; StreamSubscription? _playerCompleteSubscription; StreamSubscription? _playerStateSubscription; @override void initState() { super.initState(); _totalDuration = widget.totalDuration; _playerStateSubscription = _audioPlayer.onPlayerStateChanged.listen((state) { if(mounted && state == PlayerState.playing) { setState(() => _isPlaying = true); } else if (mounted) { setState(() => _isPlaying = false); } }); _positionSubscription = _audioPlayer.onPositionChanged.listen((position) { if (mounted) setState(() => _playDuration = position); }); _playerCompleteSubscription = _audioPlayer.onPlayerComplete.listen((_) { if (mounted) setState(() { _playDuration = Duration.zero; _isPlaying = false; }); }); } @override void dispose() { _positionSubscription?.cancel(); _playerCompleteSubscription?.cancel(); _playerStateSubscription?.cancel(); _audioPlayer.dispose(); super.dispose(); } Future _togglePlay() async { if (_isPlaying) { await _audioPlayer.pause(); } else { await _audioPlayer.play(DeviceFileSource(widget.audioPath)); if (_totalDuration == Duration.zero) { final duration = await _audioPlayer.getDuration(); if (mounted && duration != null) { setState(() => _totalDuration = duration); } } } } @override Widget build(BuildContext context) { return Padding( key: const ValueKey('audio_preview'), padding: const EdgeInsets.fromLTRB(16, 8, 16, 0), child: Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: AppColors.cardBackground, borderRadius: BorderRadius.circular(16), border: Border.all(color: AppColors.primary.withOpacity(0.3)), ), child: Row( children: [ GestureDetector( onTap: _togglePlay, child: Container( width: 40, height: 40, decoration: BoxDecoration( color: AppColors.primary, shape: BoxShape.circle, ), child: Icon( _isPlaying ? Icons.pause : Icons.play_arrow, color: AppColors.surface, size: 20, ), ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.mic, size: 16, color: AppColors.primary), const SizedBox(width: 4), Text( 'Voice Message', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: AppColors.textSecondary, ), ), ], ), const SizedBox(height: 4), Row( children: [ Expanded( child: LinearProgressIndicator( value: (_totalDuration.inMilliseconds > 0 ? _playDuration.inMilliseconds / _totalDuration.inMilliseconds : 0.0) .clamp(0.0, 1.0), backgroundColor: AppColors.greyBorder, valueColor: AlwaysStoppedAnimation( AppColors.primary), minHeight: 2, ), ), const SizedBox(width: 8), Text( widget.formatDuration(_playDuration), style: TextStyle( fontSize: 12, color: AppColors.textSecondary, ), ), Text( ' / ${widget.formatDuration(_totalDuration)}', style: TextStyle( fontSize: 12, color: AppColors.textSecondary, ), ), ], ), ], ), ), const SizedBox(width: 8), GestureDetector( onTap: widget.onDelete, child: Container( padding: const EdgeInsets.all(6), decoration: BoxDecoration( color: AppColors.errorColor.withOpacity(0.1), shape: BoxShape.circle, ), child: Icon( Icons.delete_outline, color: AppColors.errorColor, size: 18, ), ), ), ], ), ), ); } }