mirror of
https://github.com/R0m1k3/xtremflow.git
synced 2026-10-22 06:28:04 +02:00
feat: add IPTV player screen with HLS streaming, playback controls, and premium features like audio track and aspect ratio selection.
This commit is contained in:
1 parent
33778c5a3a
commit
21ec95e05b
1 file changed
+106
-53
@@ -62,6 +62,7 @@ class _PlayerScreenState extends ConsumerState<PlayerScreen> {
|
|||||||
double _totalDuration = 1;
|
double _totalDuration = 1;
|
||||||
double _volume = 1.0;
|
double _volume = 1.0;
|
||||||
double _previousVolume = 1.0;
|
double _previousVolume = 1.0;
|
||||||
|
bool _isFullscreen = false;
|
||||||
Timer? _controlsTimer;
|
Timer? _controlsTimer;
|
||||||
StreamSubscription? _messageSubscription;
|
StreamSubscription? _messageSubscription;
|
||||||
|
|
||||||
@@ -258,6 +259,26 @@ class _PlayerScreenState extends ConsumerState<PlayerScreen> {
|
|||||||
Future.microtask(() => _initializePlayer());
|
Future.microtask(() => _initializePlayer());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _toggleFullscreen() {
|
||||||
|
final document = html.document;
|
||||||
|
final docElement = document.documentElement;
|
||||||
|
|
||||||
|
if (_isFullscreen) {
|
||||||
|
// Exit fullscreen
|
||||||
|
document.exitFullscreen();
|
||||||
|
setState(() {
|
||||||
|
_isFullscreen = false;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Enter fullscreen
|
||||||
|
docElement?.requestFullscreen();
|
||||||
|
setState(() {
|
||||||
|
_isFullscreen = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_onHover();
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _initializePlayer({double? startTimeOverride}) async {
|
Future<void> _initializePlayer({double? startTimeOverride}) async {
|
||||||
try {
|
try {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -786,30 +807,98 @@ class _PlayerScreenState extends ConsumerState<PlayerScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// Play/Pause Button (Centered)
|
// Playback Controls (Centered Row: Previous - Play/Pause - Next)
|
||||||
Positioned(
|
Positioned(
|
||||||
top: null, // Removed top: 0
|
top: null,
|
||||||
bottom: 150, // Moved lower
|
bottom: 150,
|
||||||
left: 0,
|
left: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: PointerInterceptor(
|
child: PointerInterceptor(
|
||||||
child: InkWell(
|
child: Row(
|
||||||
onTap: _togglePlayPause,
|
mainAxisSize: MainAxisSize.min,
|
||||||
borderRadius: BorderRadius.circular(50),
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
child: Container(
|
children: [
|
||||||
padding: const EdgeInsets.all(10), // Reduced from 12
|
// Previous Channel Button
|
||||||
decoration: BoxDecoration(
|
if (widget.channels != null && widget.channels!.isNotEmpty)
|
||||||
color: Colors.black.withOpacity(0.5),
|
InkWell(
|
||||||
shape: BoxShape.circle,
|
onTap: _playPrevious,
|
||||||
border: Border.all(color: Colors.white, width: 2),
|
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,
|
// Next Channel Button
|
||||||
color: Colors.white,
|
if (widget.channels != null && widget.channels!.isNotEmpty)
|
||||||
size: 40, // Reduced from 48
|
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,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -862,43 +951,7 @@ 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
|
// EPG Overlay
|
||||||
if (widget.streamType == StreamType.live)
|
if (widget.streamType == StreamType.live)
|
||||||
|
|||||||
Reference in new issue
Block a user