189 lines
6.0 KiB
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,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
} |