import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.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/services/media/voice.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/skeleton_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:just_audio/just_audio.dart'; class DidvanVoiceDetailCard extends StatefulWidget { final DidvanVoiceModel didvanVoice; const DidvanVoiceDetailCard({ super.key, required this.didvanVoice, }); @override State createState() => _DidvanVoiceDetailCardState(); } class _DidvanVoiceDetailCardState extends State { late AudioPlayer _audioPlayer; bool _isInitialized = false; @override void initState() { super.initState(); _initializeAudio(); } void _initializeAudio() async { _audioPlayer = VoiceService.audioPlayer; // برای یک لقمه استراتژی، لینک مستقیم از سرور استفاده می‌شود final audioUrl = widget.didvanVoice.file.startsWith('http') ? widget.didvanVoice.file : '${RequestHelper.baseUrl}${widget.didvanVoice.file}?accessToken=${RequestService.token}'; debugPrint('🎙️ Didvan Voice Audio URL: $audioUrl'); try { VoiceService.src = audioUrl; await _audioPlayer.setUrl(audioUrl); if (mounted) { setState(() { _isInitialized = true; }); } } catch (e) { debugPrint('❌ Audio initialization error: $e'); } } @override void dispose() { super.dispose(); } String _formatDuration(Duration? duration) { if (duration == null) return '00:00'; String twoDigits(int n) => n.toString().padLeft(2, '0'); final minutes = twoDigits(duration.inMinutes.remainder(60)); final seconds = twoDigits(duration.inSeconds.remainder(60)); return '$minutes:$seconds'; } @override Widget build(BuildContext context) { final imageUrl = '${RequestHelper.baseUrl}${widget.didvanVoice.image}'; return Container( margin: const EdgeInsets.symmetric(horizontal: 16), decoration: BoxDecoration( color: DesignConfig.isDark ? const Color.fromARGB(255, 62, 62, 62) : const Color.fromARGB(255, 235, 235, 235), borderRadius: BorderRadius.circular(16), ), child: Column( children: [ Padding( padding: const EdgeInsets.fromLTRB(8, 8, 8, 2), child: ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(16)), child: SkeletonImage( imageUrl: imageUrl, width: double.infinity, height: 200, borderRadius: const BorderRadius.vertical(top: Radius.circular(16)), ), ), ), Padding( padding: const EdgeInsets.all(8), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ DidvanText( widget.didvanVoice.title, style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: DesignConfig.isDark ? Colors.white : Colors.black, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 16), Directionality( textDirection: TextDirection.ltr, child: StreamBuilder( stream: _audioPlayer.durationStream, builder: (context, snapshot) { final duration = snapshot.data ?? Duration.zero; return StreamBuilder( stream: _audioPlayer.positionStream, builder: (context, snapshot) { var position = snapshot.data ?? Duration.zero; if (position > duration) { position = duration; } return Row( children: [ SizedBox( width: 45, child: Text( _formatDuration(position), style: TextStyle( fontSize: 12, // ignore: deprecated_member_use color: Theme.of(context) .colorScheme .caption), textAlign: TextAlign.left, ), ), const SizedBox(width: 8), Expanded( child: SliderTheme( data: const SliderThemeData( thumbShape: RoundSliderThumbShape( enabledThumbRadius: 6), overlayShape: RoundSliderOverlayShape( overlayRadius: 12), trackHeight: 4, thumbColor: Color.fromARGB(255, 0, 126, 167), activeTrackColor: Color.fromARGB(255, 0, 126, 167), inactiveTrackColor: Colors.grey, ), child: Slider( value: position.inMilliseconds.toDouble(), max: duration.inMilliseconds.toDouble() > 0 ? duration.inMilliseconds.toDouble() : 1.0, onChanged: (value) { _audioPlayer.seek(Duration( milliseconds: value.toInt())); }, ), ), ), const SizedBox(width: 8), SizedBox( width: 45, child: Text( _formatDuration(duration), style: TextStyle( fontSize: 12, // ignore: deprecated_member_use color: Theme.of(context) .colorScheme .caption), textAlign: TextAlign.right, ), ), ], ); }, ); }, ), ), const SizedBox(height: 16), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( onPressed: () { final newPosition = _audioPlayer.position + const Duration(seconds: 10); _audioPlayer.seek(newPosition); }, icon: SvgPicture.asset( 'lib/assets/icons/forward-10-seconds.svg', width: 30, height: 30, color: DesignConfig.isDark ? const Color.fromARGB(255, 117, 117, 117) : null), ), const SizedBox(width: 15), StreamBuilder( 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 const SizedBox( width: 50, height: 50, child: Center( child: CircularProgressIndicator( color: Color(0xFF3B82F6), strokeWidth: 3, ), ), ); } return GestureDetector( onTap: () { if (playing) { _audioPlayer.pause(); } else { _audioPlayer.play(); } }, child: SvgPicture.asset( playing ? 'lib/assets/icons/pause-circle.svg' : 'lib/assets/icons/play.svg', width: 50, height: 50, ), ); }, ), const SizedBox(width: 15), IconButton( onPressed: () { final newPosition = _audioPlayer.position - const Duration(seconds: 5); _audioPlayer.seek(newPosition < Duration.zero ? Duration.zero : newPosition); }, icon: SvgPicture.asset( 'lib/assets/icons/backward-5-seconds.svg', width: 30, height: 30, color: DesignConfig.isDark ? const Color.fromARGB(255, 117, 117, 117) : null), ), ], ), ], ), ), ], ), ); } }