proxybuy-flutter/lib/widgets/chat_message_audio_player.dart

157 lines
4.9 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:audioplayers/audioplayers.dart';
import 'package:lba/res/colors.dart';
class ChatMessageAudioPlayer extends StatefulWidget {
final String audioPath;
final bool isUser;
final Duration? audioDuration;
const ChatMessageAudioPlayer({
super.key,
required this.audioPath,
required this.isUser,
this.audioDuration,
});
@override
State<ChatMessageAudioPlayer> createState() => _ChatMessageAudioPlayerState();
}
class _ChatMessageAudioPlayerState extends State<ChatMessageAudioPlayer> {
final AudioPlayer _audioPlayer = AudioPlayer();
PlayerState _playerState = PlayerState.stopped;
Duration _currentPosition = Duration.zero;
Duration _totalDuration = Duration.zero;
StreamSubscription? _durationSubscription;
StreamSubscription? _positionSubscription;
StreamSubscription? _playerCompleteSubscription;
StreamSubscription? _playerStateSubscription;
@override
void initState() {
super.initState();
if (widget.audioDuration != null) {
_totalDuration = widget.audioDuration!;
}
_playerStateSubscription =
_audioPlayer.onPlayerStateChanged.listen((state) {
if (mounted) {
setState(() {
_playerState = state;
});
}
});
_positionSubscription = _audioPlayer.onPositionChanged.listen((position) {
if (mounted) {
setState(() {
_currentPosition = position;
});
}
});
_playerCompleteSubscription = _audioPlayer.onPlayerComplete.listen((event) {
if (mounted) {
setState(() {
_currentPosition = Duration.zero;
});
}
});
}
@override
void dispose() {
_durationSubscription?.cancel();
_positionSubscription?.cancel();
_playerCompleteSubscription?.cancel();
_playerStateSubscription?.cancel();
_audioPlayer.dispose();
super.dispose();
}
Future<void> _togglePlayPause() async {
if (_playerState == PlayerState.playing) {
await _audioPlayer.pause();
} else {
await _audioPlayer.play(DeviceFileSource(widget.audioPath));
_audioPlayer.getDuration().then((duration) {
if (mounted && duration != null) {
setState(() {
_totalDuration = duration;
});
}
});
}
}
String _formatDuration(Duration duration) {
String twoDigits(int n) => n.toString().padLeft(2, "0");
String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
return "$twoDigitMinutes:$twoDigitSeconds";
}
@override
Widget build(BuildContext context) {
final color = widget.isUser ? AppColors.surface : AppColors.primary;
final backgroundColor =
widget.isUser ? AppColors.surface.withOpacity(0.24) : AppColors.divider;
final bool isPlaying = _playerState == PlayerState.playing;
return Row(
mainAxisSize: MainAxisSize.min,
children: [
GestureDetector(
onTap: _togglePlayPause,
child: Icon(
isPlaying ? Icons.pause_circle_filled : Icons.play_circle_filled,
color: color,
size: 32,
),
),
const SizedBox(width: 8),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
SliderTheme(
data: SliderTheme.of(context).copyWith(
trackHeight: 3.0,
thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 6.0),
overlayShape: const RoundSliderOverlayShape(overlayRadius: 12.0),
thumbColor: color,
activeTrackColor: color,
inactiveTrackColor: backgroundColor,
overlayColor: color.withOpacity(0.2),
),
child: Slider(
value: _currentPosition.inMilliseconds.toDouble().clamp(0.0,
_totalDuration.inMilliseconds.toDouble()),
min: 0.0,
max: _totalDuration.inMilliseconds.toDouble() > 0 ? _totalDuration.inMilliseconds.toDouble() : 1.0,
onChanged: (value) async {
final position = Duration(milliseconds: value.toInt());
await _audioPlayer.seek(position);
},
),
),
const SizedBox(height: 2),
Text(
"${_formatDuration(_currentPosition)} / ${_formatDuration(_totalDuration)}",
style: TextStyle(
color: widget.isUser ? AppColors.surface.withOpacity(0.7) : AppColors.textPrimary.withOpacity(0.45),
fontSize: 11.0,
),
),
],
),
),
],
);
}
}