mirror of
https://github.com/R0m1k3/xtremflow.git
synced 2026-10-22 14:38:06 +02:00
feat: add IPTV player screen with support for live, VOD, and series streams using iframe-based playback.
This commit is contained in:
1 parent
b6a0016c00
commit
440f15faa6
1 file changed
+113
-113
@@ -348,57 +348,59 @@ class _PlayerScreenState extends ConsumerState<PlayerScreen> {
|
|||||||
top: _showControls ? 24 : -100,
|
top: _showControls ? 24 : -100,
|
||||||
left: 24,
|
left: 24,
|
||||||
right: 24,
|
right: 24,
|
||||||
child: GlassContainer(
|
child: PointerInterceptor(
|
||||||
height: 72,
|
child: GlassContainer(
|
||||||
borderRadius: 24,
|
height: 72,
|
||||||
opacity: 0.15,
|
borderRadius: 24,
|
||||||
border: true,
|
opacity: 0.15,
|
||||||
borderColor: Colors.white.withOpacity(0.1),
|
border: true,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
borderColor: Colors.white.withOpacity(0.1),
|
||||||
child: Row(
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
children: [
|
child: Row(
|
||||||
_buildGlassIconButton(
|
children: [
|
||||||
icon: Icons.arrow_back_rounded,
|
_buildGlassIconButton(
|
||||||
onTap: () => Navigator.pop(context),
|
icon: Icons.arrow_back_rounded,
|
||||||
),
|
onTap: () => Navigator.pop(context),
|
||||||
const SizedBox(width: 16),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
widget.channels != null
|
|
||||||
? widget.channels![_currentIndex].name
|
|
||||||
: widget.title,
|
|
||||||
style: GoogleFonts.outfit(
|
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
if (widget.channels != null)
|
|
||||||
Text(
|
|
||||||
'Live TV',
|
|
||||||
style: GoogleFonts.inter(
|
|
||||||
color: AppColors.primary,
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
letterSpacing: 1.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(width: 16),
|
||||||
const SizedBox(width: 16),
|
Expanded(
|
||||||
_buildGlassIconButton(
|
child: Column(
|
||||||
icon: _isMuted
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
? Icons.volume_off_rounded
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
: Icons.volume_up_rounded,
|
children: [
|
||||||
onTap: _toggleMute,
|
Text(
|
||||||
),
|
widget.channels != null
|
||||||
],
|
? widget.channels![_currentIndex].name
|
||||||
|
: widget.title,
|
||||||
|
style: GoogleFonts.outfit(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
if (widget.channels != null)
|
||||||
|
Text(
|
||||||
|
'Live TV',
|
||||||
|
style: GoogleFonts.inter(
|
||||||
|
color: AppColors.primary,
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
letterSpacing: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
_buildGlassIconButton(
|
||||||
|
icon: _isMuted
|
||||||
|
? Icons.volume_off_rounded
|
||||||
|
: Icons.volume_up_rounded,
|
||||||
|
onTap: _toggleMute,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -410,49 +412,51 @@ class _PlayerScreenState extends ConsumerState<PlayerScreen> {
|
|||||||
bottom: 40,
|
bottom: 40,
|
||||||
left: 0,
|
left: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
child: Center(
|
child: PointerInterceptor(
|
||||||
child: GlassContainer(
|
child: Center(
|
||||||
borderRadius: 30,
|
child: GlassContainer(
|
||||||
opacity: 0.15,
|
borderRadius: 30,
|
||||||
border: true,
|
opacity: 0.15,
|
||||||
borderColor: Colors.white.withOpacity(0.1),
|
border: true,
|
||||||
padding: const EdgeInsets.symmetric(
|
borderColor: Colors.white.withOpacity(0.1),
|
||||||
horizontal: 24, vertical: 12),
|
padding: const EdgeInsets.symmetric(
|
||||||
child: Row(
|
horizontal: 24, vertical: 12),
|
||||||
mainAxisSize: MainAxisSize.min,
|
child: Row(
|
||||||
children: [
|
mainAxisSize: MainAxisSize.min,
|
||||||
// Previous Channel
|
children: [
|
||||||
if (widget.channels != null &&
|
// Previous Channel
|
||||||
widget.channels!.length > 1) ...[
|
if (widget.channels != null &&
|
||||||
_buildGlassIconButton(
|
widget.channels!.length > 1) ...[
|
||||||
icon: Icons.skip_previous_rounded,
|
_buildGlassIconButton(
|
||||||
onTap: _previousChannel,
|
icon: Icons.skip_previous_rounded,
|
||||||
transparent: true,
|
onTap: _previousChannel,
|
||||||
),
|
transparent: true,
|
||||||
const SizedBox(width: 24),
|
),
|
||||||
],
|
const SizedBox(width: 24),
|
||||||
|
],
|
||||||
|
|
||||||
// Play/Pause
|
// Play/Pause
|
||||||
_buildGlassIconButton(
|
|
||||||
icon: _isPlaying
|
|
||||||
? Icons.pause_rounded
|
|
||||||
: Icons.play_arrow_rounded,
|
|
||||||
onTap: _togglePlayPause,
|
|
||||||
size: 56,
|
|
||||||
iconSize: 32,
|
|
||||||
),
|
|
||||||
|
|
||||||
// Next Channel
|
|
||||||
if (widget.channels != null &&
|
|
||||||
widget.channels!.length > 1) ...[
|
|
||||||
const SizedBox(width: 24),
|
|
||||||
_buildGlassIconButton(
|
_buildGlassIconButton(
|
||||||
icon: Icons.skip_next_rounded,
|
icon: _isPlaying
|
||||||
onTap: _nextChannel,
|
? Icons.pause_rounded
|
||||||
transparent: true,
|
: Icons.play_arrow_rounded,
|
||||||
|
onTap: _togglePlayPause,
|
||||||
|
size: 56,
|
||||||
|
iconSize: 32,
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// Next Channel
|
||||||
|
if (widget.channels != null &&
|
||||||
|
widget.channels!.length > 1) ...[
|
||||||
|
const SizedBox(width: 24),
|
||||||
|
_buildGlassIconButton(
|
||||||
|
icon: Icons.skip_next_rounded,
|
||||||
|
onTap: _nextChannel,
|
||||||
|
transparent: true,
|
||||||
|
),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -724,31 +728,27 @@ class _PlayerScreenState extends ConsumerState<PlayerScreen> {
|
|||||||
double size = 48,
|
double size = 48,
|
||||||
double iconSize = 24,
|
double iconSize = 24,
|
||||||
}) {
|
}) {
|
||||||
// Simplified button with direct Material/InkWell and PointerInterceptor
|
// Button with direct Material/InkWell - PointerInterceptor is on outer container
|
||||||
// Simplified button with direct Material/InkWell and PointerInterceptor
|
return Material(
|
||||||
// We wrap the specific button to ensure it catches events over the iframe
|
color: transparent ? Colors.transparent : Colors.white.withOpacity(0.1),
|
||||||
return PointerInterceptor(
|
shape: const CircleBorder(),
|
||||||
child: Material(
|
child: InkWell(
|
||||||
color: transparent ? Colors.transparent : Colors.white.withOpacity(0.1),
|
onTap: () {
|
||||||
shape: const CircleBorder(),
|
print('[PlayerScreen] Button tapped: $icon');
|
||||||
child: InkWell(
|
onTap();
|
||||||
onTap: () {
|
},
|
||||||
print('[PlayerScreen] Button tapped: $icon');
|
customBorder: const CircleBorder(),
|
||||||
onTap();
|
child: Container(
|
||||||
},
|
width: size,
|
||||||
customBorder: const CircleBorder(),
|
height: size,
|
||||||
child: Container(
|
alignment: Alignment.center,
|
||||||
width: size,
|
decoration: BoxDecoration(
|
||||||
height: size,
|
shape: BoxShape.circle,
|
||||||
alignment: Alignment.center,
|
border: transparent
|
||||||
decoration: BoxDecoration(
|
? null
|
||||||
shape: BoxShape.circle,
|
: Border.all(color: Colors.white.withOpacity(0.1)),
|
||||||
border: transparent
|
|
||||||
? null
|
|
||||||
: Border.all(color: Colors.white.withOpacity(0.1)),
|
|
||||||
),
|
|
||||||
child: Icon(icon, color: Colors.white, size: iconSize),
|
|
||||||
),
|
),
|
||||||
|
child: Icon(icon, color: Colors.white, size: iconSize),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in new issue
Block a user