mirror of
https://github.com/R0m1k3/xtremflow.git
synced 2026-10-11 17:30:00 +02:00
feat: implement PlayerScreen for IPTV streams with web iframe integration and full playback controls.
This commit is contained in:
1 parent
643bf7e761
commit
f076e1859d
1 file changed
+104
-55
@@ -62,6 +62,7 @@ class _PlayerScreenState extends ConsumerState<PlayerScreen> {
|
||||
double _totalDuration = 1;
|
||||
double _volume = 1.0;
|
||||
double _previousVolume = 1.0;
|
||||
bool _isFullscreen = false;
|
||||
Timer? _controlsTimer;
|
||||
StreamSubscription? _messageSubscription;
|
||||
|
||||
@@ -258,6 +259,24 @@ class _PlayerScreenState extends ConsumerState<PlayerScreen> {
|
||||
Future.microtask(() => _initializePlayer());
|
||||
}
|
||||
|
||||
void _toggleFullscreen() {
|
||||
final document = html.document;
|
||||
final docElement = document.documentElement;
|
||||
|
||||
if (_isFullscreen) {
|
||||
document.exitFullscreen();
|
||||
setState(() {
|
||||
_isFullscreen = false;
|
||||
});
|
||||
} else {
|
||||
docElement?.requestFullscreen();
|
||||
setState(() {
|
||||
_isFullscreen = true;
|
||||
});
|
||||
}
|
||||
_onHover();
|
||||
}
|
||||
|
||||
Future<void> _initializePlayer({double? startTimeOverride}) async {
|
||||
try {
|
||||
setState(() {
|
||||
@@ -786,30 +805,98 @@ class _PlayerScreenState extends ConsumerState<PlayerScreen> {
|
||||
),
|
||||
),
|
||||
|
||||
// Play/Pause Button (Centered)
|
||||
// Playback Controls (Centered Row: Previous - Play/Pause - Next - Fullscreen)
|
||||
Positioned(
|
||||
top: null, // Removed top: 0
|
||||
bottom: 150, // Moved lower
|
||||
top: null,
|
||||
bottom: 150,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Center(
|
||||
child: PointerInterceptor(
|
||||
child: InkWell(
|
||||
onTap: _togglePlayPause,
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(10), // Reduced from 12
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(color: Colors.white, width: 2),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
// Previous Channel Button
|
||||
if (widget.channels != null && widget.channels!.isNotEmpty)
|
||||
InkWell(
|
||||
onTap: _playPrevious,
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.skip_previous,
|
||||
color: Colors.white,
|
||||
size: 32,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (widget.channels != null && widget.channels!.isNotEmpty)
|
||||
const SizedBox(width: 24),
|
||||
|
||||
// Play/Pause Button
|
||||
InkWell(
|
||||
onTap: _togglePlayPause,
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(color: Colors.white, width: 2),
|
||||
),
|
||||
child: Icon(
|
||||
_isPlaying ? Icons.pause : Icons.play_arrow,
|
||||
color: Colors.white,
|
||||
size: 40,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Icon(
|
||||
_isPlaying ? Icons.pause : Icons.play_arrow,
|
||||
color: Colors.white,
|
||||
size: 40, // Reduced from 48
|
||||
|
||||
// Next Channel Button
|
||||
if (widget.channels != null && widget.channels!.isNotEmpty)
|
||||
const SizedBox(width: 24),
|
||||
if (widget.channels != null && widget.channels!.isNotEmpty)
|
||||
InkWell(
|
||||
onTap: _playNext,
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.skip_next,
|
||||
color: Colors.white,
|
||||
size: 32,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Fullscreen Button
|
||||
const SizedBox(width: 32),
|
||||
InkWell(
|
||||
onTap: _toggleFullscreen,
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
_isFullscreen ? Icons.fullscreen_exit : Icons.fullscreen,
|
||||
color: Colors.white,
|
||||
size: 28,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -861,44 +948,6 @@ class _PlayerScreenState extends ConsumerState<PlayerScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Previous Channel (Left Top Zone)
|
||||
if (widget.channels != null && widget.channels!.isNotEmpty)
|
||||
Positioned(
|
||||
left: 0,
|
||||
top: 80,
|
||||
bottom: null,
|
||||
height: 200,
|
||||
width: 100,
|
||||
child: PointerInterceptor(
|
||||
child: InkWell(
|
||||
onTap: _playPrevious,
|
||||
hoverColor: Colors.black12,
|
||||
child: const Center(
|
||||
child: Icon(Icons.arrow_back_ios, color: Colors.white, size: 48),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Next Channel (Right Top Zone)
|
||||
if (widget.channels != null && widget.channels!.isNotEmpty)
|
||||
Positioned(
|
||||
right: 0,
|
||||
top: 80, // Moved to top
|
||||
bottom: null,
|
||||
height: 200,
|
||||
width: 100,
|
||||
child: PointerInterceptor(
|
||||
child: InkWell(
|
||||
onTap: _playNext,
|
||||
hoverColor: Colors.black12,
|
||||
child: const Center(
|
||||
child: Icon(Icons.arrow_forward_ios, color: Colors.white, size: 48),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// EPG Overlay
|
||||
if (widget.streamType == StreamType.live)
|
||||
|
||||
Reference in new issue
Block a user