mirror of
https://github.com/R0m1k3/xtremflow.git
synced 2026-10-24 15:38:04 +02:00
feat: implement mobile dashboard screen with live TV tab and channel filtering functionality
This commit is contained in:
1 parent
ddb8110c40
commit
77dc9053bb
2 files changed
+27
-71
No files matched your search
@@ -34,16 +34,23 @@ class _MobileDashboardScreenState extends ConsumerState<MobileDashboardScreen> {
|
|||||||
_currentIndex = index;
|
_currentIndex = index;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: IndexedStack(
|
child: _buildActiveTab(),
|
||||||
index: _currentIndex,
|
|
||||||
children: [
|
|
||||||
MobileLiveTVTab(playlist: widget.playlist),
|
|
||||||
MobileMoviesTab(playlist: widget.playlist),
|
|
||||||
MobileSeriesTab(playlist: widget.playlist),
|
|
||||||
const MobileSettingsTab(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildActiveTab() {
|
||||||
|
switch (_currentIndex) {
|
||||||
|
case 0:
|
||||||
|
return MobileLiveTVTab(playlist: widget.playlist);
|
||||||
|
case 1:
|
||||||
|
return MobileMoviesTab(playlist: widget.playlist);
|
||||||
|
case 2:
|
||||||
|
return MobileSeriesTab(playlist: widget.playlist);
|
||||||
|
case 3:
|
||||||
|
return const MobileSettingsTab();
|
||||||
|
default:
|
||||||
|
return MobileLiveTVTab(playlist: widget.playlist);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -292,7 +292,6 @@ class _MobileLiveTVTabState extends ConsumerState<MobileLiveTVTab> {
|
|||||||
final channel = displayedChannels[index];
|
final channel = displayedChannels[index];
|
||||||
return _MobileChannelTile(
|
return _MobileChannelTile(
|
||||||
channel: channel,
|
channel: channel,
|
||||||
playlist: widget.playlist,
|
|
||||||
onTap: () => _playChannel(
|
onTap: () => _playChannel(
|
||||||
context,
|
context,
|
||||||
channel,
|
channel,
|
||||||
@@ -329,31 +328,22 @@ class _MobileLiveTVTabState extends ConsumerState<MobileLiveTVTab> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MobileChannelTile extends ConsumerWidget {
|
class _MobileChannelTile extends StatelessWidget {
|
||||||
final Channel channel;
|
final Channel channel;
|
||||||
final PlaylistConfig playlist;
|
|
||||||
final VoidCallback onTap;
|
final VoidCallback onTap;
|
||||||
|
|
||||||
const _MobileChannelTile({
|
const _MobileChannelTile({
|
||||||
required this.channel,
|
required this.channel,
|
||||||
required this.playlist,
|
|
||||||
required this.onTap,
|
required this.onTap,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context) {
|
||||||
final iconUrl =
|
final iconUrl =
|
||||||
channel.streamIcon.isNotEmpty && channel.streamIcon.startsWith('http')
|
channel.streamIcon.isNotEmpty && channel.streamIcon.startsWith('http')
|
||||||
? '/api/xtream/${channel.streamIcon}'
|
? '/api/xtream/${channel.streamIcon}'
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
// Fetch EPG
|
|
||||||
final epgAsync = ref.watch(
|
|
||||||
epgByPlaylistProvider(
|
|
||||||
EpgRequestKey(playlist: playlist, streamId: channel.streamId),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
return GlassContainer(
|
return GlassContainer(
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
opacity: 0.15,
|
opacity: 0.15,
|
||||||
@@ -384,56 +374,15 @@ class _MobileChannelTile extends ConsumerWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Text(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
channel.name,
|
||||||
children: [
|
style: GoogleFonts.inter(
|
||||||
Text(
|
color: Colors.white,
|
||||||
channel.name,
|
fontWeight: FontWeight.w500,
|
||||||
style: GoogleFonts.inter(
|
fontSize: 14,
|
||||||
color: Colors.white,
|
),
|
||||||
fontWeight: FontWeight.w500,
|
maxLines: 1,
|
||||||
fontSize: 14,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
|
|
||||||
// EPG Only (Replaces Channel Number)
|
|
||||||
epgAsync.when(
|
|
||||||
data: (epgList) {
|
|
||||||
if (epgList.isEmpty) {
|
|
||||||
return Text(
|
|
||||||
'No Info',
|
|
||||||
style: GoogleFonts.inter(
|
|
||||||
color: Colors.white38,
|
|
||||||
fontSize: 12,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
final current = epgList.first;
|
|
||||||
return Text(
|
|
||||||
current.title,
|
|
||||||
style: GoogleFonts.inter(
|
|
||||||
color: const Color(
|
|
||||||
0xFFFFD700,
|
|
||||||
), // Gold/Amber for visibility check
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
loading: () => const Text(
|
|
||||||
'...',
|
|
||||||
style: TextStyle(color: Colors.white38, fontSize: 12),
|
|
||||||
),
|
|
||||||
error: (err, stack) => const Text(
|
|
||||||
'Err',
|
|
||||||
style: TextStyle(color: Colors.red, fontSize: 10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
|
|||||||
Reference in new issue
Block a user