import 'dart:async'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_video_player_plus/cached_video_player_plus.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:f0ckapp/models/item.dart'; import 'package:f0ckapp/widgets/video_controls_overlay.dart'; import 'package:f0ckapp/controller/settingscontroller.dart'; import 'package:f0ckapp/controller/mediacontroller.dart'; class VideoWidget extends StatefulWidget { final MediaItem details; final bool isActive; final bool fullScreen; final VoidCallback? onInitialized; const VideoWidget({ super.key, required this.details, required this.isActive, this.fullScreen = false, this.onInitialized, }); @override State createState() => _VideoWidgetState(); } class _VideoWidgetState extends State { final MediaController mediaController = Get.find(); final SettingsController settingsController = Get.find(); late CachedVideoPlayerPlusController videoController; late Worker _muteWorker; late Worker _timerResetWorker; late Worker _hideControlsWorker; bool _showControls = false; Timer? _hideControlsTimer; @override void initState() { super.initState(); _initController(); _muteWorker = ever(settingsController.muted, (bool muted) { if (videoController.value.isInitialized) { videoController.setVolume(muted ? 0.0 : 1.0); } }); _timerResetWorker = ever(settingsController.videoControlsTimerNotifier, ( _, ) { if (widget.isActive && mounted) { if (!_showControls) { setState(() => _showControls = true); } _startHideControlsTimer(); } }); _hideControlsWorker = ever(settingsController.hideControlsNotifier, (_) { if (mounted && _showControls) { setState(() => _showControls = false); _hideControlsTimer?.cancel(); } }); } Future _initController() async { videoController = CachedVideoPlayerPlusController.networkUrl( Uri.parse(widget.details.mediaUrl), videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true), ); await videoController.initialize(); widget.onInitialized?.call(); if (!mounted) return; videoController.setLooping(true); videoController.setVolume(settingsController.muted.value ? 0.0 : 1.0); if (widget.isActive) { videoController.play(); } } @override void didUpdateWidget(covariant VideoWidget oldWidget) { super.didUpdateWidget(oldWidget); if (widget.details.mediaUrl != oldWidget.details.mediaUrl) { videoController.dispose(); _initController(); return; } if (widget.isActive != oldWidget.isActive) { if (widget.isActive) { videoController.play(); } else { videoController.pause(); } } } @override void dispose() { _muteWorker.dispose(); _timerResetWorker.dispose(); _hideControlsWorker.dispose(); videoController.dispose(); _hideControlsTimer?.cancel(); super.dispose(); } void _startHideControlsTimer() { _hideControlsTimer?.cancel(); _hideControlsTimer = Timer(const Duration(seconds: 3), () { if (mounted) { setState(() => _showControls = false); } }); } void _onTap({bool ctrlButton = false}) { if (ctrlButton) { _startHideControlsTimer(); return; } final bool newShowState = !_showControls; setState(() => _showControls = newShowState); if (newShowState) { _startHideControlsTimer(); } else { _hideControlsTimer?.cancel(); } } @override Widget build(BuildContext context) { final bool muted = settingsController.muted.value; bool isAudio = widget.details.mime.startsWith('audio'); Widget mediaContent; if (isAudio) { mediaContent = CachedNetworkImage( imageUrl: widget.details.coverUrl, fit: BoxFit.cover, errorWidget: (c, e, s) => Image.asset( 'assets/images/music.webp', fit: BoxFit.contain, width: double.infinity, ), ); } else { mediaContent = videoController.value.isInitialized ? CachedVideoPlayerPlus(videoController) : const Center(child: CircularProgressIndicator()); } return AspectRatio( aspectRatio: videoController.value.isInitialized ? videoController.value.aspectRatio : (isAudio ? 16 / 9 : 9 / 16), child: Stack( alignment: Alignment.center, children: [ GestureDetector(onTap: _onTap, child: mediaContent), AnimatedBuilder( animation: videoController, builder: (context, child) { if (videoController.value.isInitialized && _showControls) { return Positioned.fill( child: GestureDetector( onTap: _onTap, child: Container( color: Colors.black.withValues(alpha: 0.5), child: VideoControlsOverlay( controller: videoController, onOverlayTap: () => _onTap(ctrlButton: true), muted: muted, onMuteToggle: () { settingsController.toggleMuted(); }, ), ), ), ); } return const SizedBox.shrink(); }, ), ], ), ); } }