import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:cached_video_player_plus/cached_video_player_plus.dart'; import 'package:f0ckapp/providers/media_provider.dart'; class VideoControlsOverlay extends ConsumerWidget { final CachedVideoPlayerPlusController controller; final VoidCallback button; const VideoControlsOverlay({ super.key, required this.controller, required this.button, }); @override Widget build(BuildContext context, ref) { final MediaState mediaState = ref.watch(mediaProvider); final MediaNotifier mediaNotifier = ref.read(mediaProvider.notifier); return Stack( alignment: Alignment.center, children: [ Positioned( right: 12, bottom: 12, child: _ControlButton( mediaState.muted ? Icons.volume_off : Icons.volume_up, () { button(); mediaNotifier.toggleMute(); }, size: 16, ), ), Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ _ControlButton(Icons.replay_10, () { button(); Duration newPosition = controller.value.position - const Duration(seconds: 10); if (newPosition < Duration.zero) newPosition = Duration.zero; controller.seekTo(newPosition); }), SizedBox(width: 40), _ControlButton( controller.value.isPlaying ? Icons.pause : Icons.play_arrow, () { button(); controller.value.isPlaying ? controller.pause() : controller.play(); }, size: 64, ), SizedBox(width: 40), _ControlButton(Icons.forward_10, () { button(); Duration newPosition = controller.value.position + const Duration(seconds: 10); if (newPosition > controller.value.duration) { newPosition = controller.value.duration; } controller.seekTo(newPosition); }), ], ), ), Align( alignment: Alignment.bottomCenter, child: Padding( padding: const EdgeInsets.only(bottom: 0), child: Stack( clipBehavior: Clip.none, children: [ Positioned( left: 10, bottom: 12, child: Text( '${_formatDuration(controller.value.position)} / ${_formatDuration(controller.value.duration)}', ), ), Listener( onPointerDown: (_) { button(); }, child: VideoProgressIndicator( controller, allowScrubbing: true, padding: const EdgeInsets.only(top: 25.0), colors: const VideoProgressColors( playedColor: Colors.red, backgroundColor: Colors.grey, bufferedColor: Colors.white54, ), ), ), Positioned( left: (controller.value.position.inMilliseconds / controller.value.duration.inMilliseconds) * MediaQuery.of(context).size.width - 6, bottom: -4, child: Container( width: 12, height: 12, decoration: BoxDecoration( shape: BoxShape.circle, color: Colors.red, border: Border.all(color: Colors.red, width: 2), ), ), ), ], ), ), ), ], ); } String _formatDuration(Duration duration) { String twoDigits(int n) => n.toString().padLeft(2, '0'); return "${twoDigits(duration.inMinutes % 60)}:${twoDigits(duration.inSeconds % 60)}"; } } class _ControlButton extends StatelessWidget { final IconData icon; final VoidCallback onPressed; final double size; const _ControlButton(this.icon, this.onPressed, {this.size = 24}); @override Widget build(BuildContext context) { return Container( decoration: BoxDecoration( shape: BoxShape.circle, color: Colors.black.withValues(alpha: 0.4), ), child: IconButton( icon: Icon(icon, size: size), onPressed: onPressed, ), ); } }