didvan-app/lib/views/home/main/widgets/didvan_voice_section.dart

243 lines
9.2 KiB
Dart

import 'package:didvan/models/didvan_voice_model.dart';
import 'package:didvan/services/network/request_helper.dart';
import 'package:didvan/services/network/request.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:flutter/material.dart';
import 'package:just_audio/just_audio.dart';
class DidvanVoiceSection extends StatefulWidget {
final DidvanVoiceModel didvanVoice;
const DidvanVoiceSection({
super.key,
required this.didvanVoice,
});
@override
State<DidvanVoiceSection> createState() => _DidvanVoiceSectionState();
}
class _DidvanVoiceSectionState extends State<DidvanVoiceSection> {
late AudioPlayer _audioPlayer;
bool _isInitialized = false;
@override
void initState() {
super.initState();
_initializeAudio();
}
void _initializeAudio() async {
_audioPlayer = AudioPlayer();
final audioUrl =
'${RequestHelper.baseUrl}${widget.didvanVoice.file}?accessToken=${RequestService.token}';
debugPrint('🎙️ Didvan Voice Audio URL: $audioUrl');
try {
await _audioPlayer.setUrl(audioUrl);
setState(() {
_isInitialized = true;
});
debugPrint('✅ Audio initialized successfully');
} catch (e) {
debugPrint('❌ Audio initialization error: $e');
}
}
@override
void dispose() {
_audioPlayer.dispose();
super.dispose();
}
String _formatDuration(Duration? duration) {
if (duration == null) return '00:00';
final minutes = duration.inMinutes;
final seconds = duration.inSeconds % 60;
return '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
}
@override
Widget build(BuildContext context) {
final imageUrl = '${RequestHelper.baseUrl}${widget.didvanVoice.image}';
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color.fromRGBO(0, 69, 92, 1),
borderRadius: BorderRadius.circular(12),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: Image.network(
imageUrl,
width: 80,
height: 130,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return Container(
width: 80,
height: 130,
color: Colors.grey.shade800,
child: const Icon(Icons.music_note, color: Colors.white),
);
},
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
DidvanText(
widget.didvanVoice.title,
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.white,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
onPressed: () {
final newPosition =
_audioPlayer.position + const Duration(seconds: 10);
_audioPlayer.seek(newPosition);
},
icon: const Icon(Icons.forward_10, color: Colors.white),
iconSize: 28,
),
StreamBuilder<PlayerState>(
stream: _audioPlayer.playerStateStream,
builder: (context, snapshot) {
final playerState = snapshot.data;
final processingState = playerState?.processingState ??
ProcessingState.idle;
final playing = playerState?.playing ?? false;
if (processingState == ProcessingState.loading ||
processingState == ProcessingState.buffering) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 8),
width: 48,
height: 48,
child: const CircularProgressIndicator(
color: Colors.white,
),
);
} else if (!playing) {
return IconButton(
onPressed: _audioPlayer.play,
icon: const Icon(Icons.play_circle_filled,
color: Colors.white),
iconSize: 48,
);
} else {
return IconButton(
onPressed: _audioPlayer.pause,
icon: const Icon(Icons.pause_circle_filled,
color: Colors.white),
iconSize: 48,
);
}
},
),
IconButton(
onPressed: () {
final newPosition =
_audioPlayer.position - const Duration(seconds: 10);
_audioPlayer.seek(newPosition < Duration.zero
? Duration.zero
: newPosition);
},
icon: const Icon(Icons.replay_10, color: Colors.white),
iconSize: 28,
),
],
),
StreamBuilder<Duration?>(
stream: _audioPlayer.durationStream,
builder: (context, snapshot) {
final duration = snapshot.data;
return StreamBuilder<Duration>(
stream: _audioPlayer.positionStream,
builder: (context, snapshot) {
final position = snapshot.data ?? Duration.zero;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
DidvanText(
_formatDuration(duration),
fontSize: 12,
color: Colors.white70,
),
DidvanText(
_formatDuration(position),
fontSize: 12,
color: Colors.white70,
),
],
),
);
},
);
},
),
const SizedBox(height: 4),
Directionality(
textDirection: TextDirection.ltr,
child: StreamBuilder<Duration?>(
stream: _audioPlayer.durationStream,
builder: (context, snapshot) {
final duration = snapshot.data ?? Duration.zero;
return StreamBuilder<Duration>(
stream: _audioPlayer.positionStream,
builder: (context, snapshot) {
var position = snapshot.data ?? Duration.zero;
if (position > duration) {
position = duration;
}
return SliderTheme(
data: const SliderThemeData(
thumbShape:
RoundSliderThumbShape(enabledThumbRadius: 6),
overlayShape:
RoundSliderOverlayShape(overlayRadius: 12),
trackHeight: 4,
activeTrackColor: Colors.white70,
inactiveTrackColor: Colors.white30,
thumbColor: Colors.transparent,
overlayColor: Colors.transparent,
),
child: Slider(
value: position.inMilliseconds.toDouble(),
max: duration.inMilliseconds.toDouble(),
onChanged: (value) {
_audioPlayer.seek(
Duration(milliseconds: value.toInt()));
},
),
);
},
);
},
),
),
],
),
),
],
),
);
}
}