// ignore_for_file: deprecated_member_use import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/models/overview_data.dart'; import 'package:didvan/models/studio_details_data.dart'; import 'package:didvan/services/media/media.dart'; import 'package:didvan/views/home/media/widgets/audio_waveform_progress.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'; class FeaturedPodcastCard extends StatefulWidget { final OverviewData podcast; final VoidCallback onTap; final Function(bool)? onPlayStateChanged; const FeaturedPodcastCard({ super.key, required this.podcast, required this.onTap, this.onPlayStateChanged, }); @override State createState() => _FeaturedPodcastCardState(); } class _FeaturedPodcastCardState extends State with WidgetsBindingObserver { bool _isPlaying = false; bool _isLoading = false; double _progress = 0.0; Duration _currentPosition = Duration.zero; Duration _totalDuration = Duration.zero; // ignore: prefer_final_fields var _subscriptions = []; @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); if (widget.podcast.duration != null && widget.podcast.duration! > 0) { _totalDuration = Duration(seconds: widget.podcast.duration!); } _checkCurrentPlayingState(); _setupAudioListener(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { super.didChangeAppLifecycleState(state); if (state == AppLifecycleState.paused || state == AppLifecycleState.inactive) { if (_isPlaying && MediaService.currentPodcast?.id == widget.podcast.id) { MediaService.audioPlayer.pause(); } } } void _checkCurrentPlayingState() { if (MediaService.currentPodcast?.id == widget.podcast.id) { setState(() { _isPlaying = MediaService.audioPlayer.playing; }); } } void _setupAudioListener() { _subscriptions .add(MediaService.audioPlayer.positionStream.listen((position) { if (mounted && MediaService.currentPodcast?.id == widget.podcast.id) { setState(() { _currentPosition = position; if (_totalDuration.inSeconds > 0) { _progress = position.inSeconds / _totalDuration.inSeconds; } }); } })); _subscriptions .add(MediaService.audioPlayer.durationStream.listen((duration) { if (mounted && duration != null && MediaService.currentPodcast?.id == widget.podcast.id) { setState(() { _totalDuration = duration; }); } })); _subscriptions.add(MediaService.audioPlayer.playingStream.listen((playing) { if (mounted && MediaService.currentPodcast?.id == widget.podcast.id) { setState(() { _isPlaying = playing; }); widget.onPlayStateChanged?.call(playing); } })); } Future _togglePlayPause() async { if (_isLoading) return; try { final isCurrentPodcast = MediaService.currentPodcast?.id == widget.podcast.id; if (!isCurrentPodcast) { setState(() { _isLoading = true; }); if (widget.podcast.link == null || widget.podcast.link!.isEmpty) { throw Exception('لینک پادکست معتبر نیست'); } MediaService.currentPodcast = StudioDetailsData( id: widget.podcast.id, title: widget.podcast.title, description: widget.podcast.description, image: widget.podcast.image, link: widget.podcast.link ?? '', type: widget.podcast.type, duration: widget.podcast.duration ?? 0, iframe: widget.podcast.iframe, createdAt: widget.podcast.createdAt, order: 0, marked: false, comments: 0, tags: [], ); MediaService.isPlayingFromFeaturedCard = true; await MediaService.handleAudioPlayback( audioSource: widget.podcast.link!, id: widget.podcast.id, isVoiceMessage: false, isNetworkAudio: true, ); if (mounted) { setState(() { _isPlaying = true; _isLoading = false; }); widget.onPlayStateChanged?.call(true); } } else { if (_isPlaying) { await MediaService.audioPlayer.pause(); if (mounted) { setState(() { _isPlaying = false; }); widget.onPlayStateChanged?.call(false); } } else { await MediaService.audioPlayer.play(); if (mounted) { setState(() { _isPlaying = true; }); widget.onPlayStateChanged?.call(true); } } } } catch (e) { debugPrint('Error playing podcast: $e'); if (mounted) { setState(() { _isLoading = false; _isPlaying = false; }); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('خطا در پخش پادکست: ${e.toString()}'), backgroundColor: Colors.red, duration: const Duration(seconds: 3), ), ); } } } Future _seekForward() async { final newPosition = _currentPosition + const Duration(seconds: 10); if (newPosition < _totalDuration) { await MediaService.audioPlayer.seek(newPosition); } } Future _seekBackward() async { final newPosition = _currentPosition - const Duration(seconds: 5); if (newPosition > Duration.zero) { await MediaService.audioPlayer.seek(newPosition); } else { await MediaService.audioPlayer.seek(Duration.zero); } } String _formatDuration(Duration duration) { 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 bool isCurrentlyPlaying = MediaService.currentPodcast?.id == widget.podcast.id; return Container( margin: const EdgeInsets.all(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: GestureDetector( onTap: widget.onTap, child: ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(16)), child: SkeletonImage( imageUrl: widget.podcast.image, 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.podcast.title, style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: DesignConfig.isDark ? Colors.white : Colors.black, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 16), Row( children: [ SizedBox( width: 45, child: Text( _formatDuration(_totalDuration), style: TextStyle( fontSize: 12, color: Theme.of(context).colorScheme.caption), textAlign: TextAlign.left, ), ), const SizedBox(width: 8), Expanded( child: AudioWaveformProgress( progress: isCurrentlyPlaying ? _progress.clamp(0.0, 1.0) : 0.0, isActive: isCurrentlyPlaying, onChanged: isCurrentlyPlaying ? (value) { final newPosition = Duration( seconds: (value * _totalDuration.inSeconds) .toInt(), ); MediaService.audioPlayer.seek(newPosition); } : null, ), ), const SizedBox(width: 8), SizedBox( width: 45, child: Text( _formatDuration(_currentPosition), style: TextStyle( fontSize: 12, color: Theme.of(context).colorScheme.caption), textAlign: TextAlign.right, ), ), ], ), const SizedBox(height: 16), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( onPressed: isCurrentlyPlaying ? _seekForward : null, 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), GestureDetector( onTap: _togglePlayPause, child: _isLoading ? const SizedBox( width: 50, height: 50, child: Center( child: CircularProgressIndicator( color: Color(0xFF3B82F6), strokeWidth: 3, ), ), ) : AnimatedSwitcher( duration: const Duration(milliseconds: 300), transitionBuilder: (child, animation) { return ScaleTransition( scale: animation, child: child, ); }, child: SvgPicture.asset( isCurrentlyPlaying && _isPlaying ? 'lib/assets/icons/pause-circle.svg' : 'lib/assets/icons/play.svg', key: ValueKey( isCurrentlyPlaying && _isPlaying), width: isCurrentlyPlaying && _isPlaying ? 55 : 50, height: isCurrentlyPlaying && _isPlaying ? 55 : 50, ), ), ), const SizedBox(width: 15), IconButton( onPressed: isCurrentlyPlaying ? _seekBackward : null, 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), ), ], ), ], ), ), ], ), ); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); for (var subscription in _subscriptions) { subscription.cancel(); } _subscriptions.clear(); super.dispose(); } }