proxybuy-flutter/lib/widgets/recorded_audio_preview.dart

189 lines
6.0 KiB
Dart

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<RecordedAudioPreview> {
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<void> _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<Color>(
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,
),
),
),
],
),
),
);
}
}