Merge pull request #61 from R0m1k3/antigravity

feat: implement ChannelCard widget with EPG display, interactive hove…
This commit is contained in:
LogiFlow authored and GitHub committed 2025-12-07 00:17:07 +01:00
commit 56cc0b1694
3 files changed
+67 -35

No files matched your search

+1 -1
View File
@@ -15,7 +15,7 @@ class AppColors {
static const Color background = Color(0xFF000000); static const Color background = Color(0xFF000000);
/// Very deep grey for surfaces that need to be distinct but subtle (Apple Dark Grey) /// Very deep grey for surfaces that need to be distinct but subtle (Apple Dark Grey)
static const Color surface = Color(0xFF2C2C2E); static const Color surface = Color(0xFF1C1C1E);
/// Slightly lighter grey for secondary surfaces or hover states /// Slightly lighter grey for secondary surfaces or hover states
static const Color surfaceVariant = Color(0xFF3A3A3C); static const Color surfaceVariant = Color(0xFF3A3A3C);
+60 -32
View File
@@ -48,7 +48,7 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> with SingleTi
// Glassmorphism Top Bar // Glassmorphism Top Bar
return Scaffold( return Scaffold(
extendBodyBehindAppBar: true, // Content behind app bar extendBodyBehindAppBar: true, // Content behind app bar
backgroundColor: AppColors.background, backgroundColor: Colors.transparent, // Transparent to show gradient
appBar: PreferredSize( appBar: PreferredSize(
preferredSize: const Size.fromHeight(80), preferredSize: const Size.fromHeight(80),
child: Container( child: Container(
@@ -58,19 +58,43 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> with SingleTi
begin: Alignment.topCenter, begin: Alignment.topCenter,
end: Alignment.bottomCenter, end: Alignment.bottomCenter,
colors: [ colors: [
Colors.black.withOpacity(0.9), // Darker at top Colors.black.withOpacity(0.6), // Subtler top shadow
Colors.transparent, Colors.transparent,
], ],
), ),
), ),
child: Row( child: Stack(
alignment: Alignment.center,
children: [ children: [
// Logo Area // 1. Side Elements (Logo & Profile) - Pushed to edges
_buildLogo(), Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// Logo Area
_buildLogo(),
// Search / Profile (Right Side)
Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(Icons.search, color: Colors.white),
onPressed: () {
// Global search trigger
},
),
const SizedBox(width: 8),
const CircleAvatar(
radius: 16,
backgroundColor: Colors.white24,
child: Icon(Icons.person, size: 18, color: Colors.white),
),
],
),
],
),
const Spacer(), // 2. Centered Navigation Tabs (Pills)
// Centered Navigation Tabs (Pills)
Container( Container(
height: 50, height: 50,
decoration: BoxDecoration( decoration: BoxDecoration(
@@ -108,35 +132,39 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> with SingleTi
}), }),
), ),
), ),
const Spacer(),
// Search / Profile (Right Side)
IconButton(
icon: const Icon(Icons.search, color: Colors.white),
onPressed: () {
// Global search trigger
},
),
const SizedBox(width: 8),
const CircleAvatar(
radius: 16,
backgroundColor: Colors.white24,
child: Icon(Icons.person, size: 18, color: Colors.white),
),
], ],
), ),
), ),
), ),
body: TabBarView( body: Stack(
controller: _tabController,
physics: const NeverScrollableScrollPhysics(), // Disable swipe to avoid gesture conflicts
children: [ children: [
// Padding top to account for the transparent AppBar // Background Gradient (Apple TV Depth Effect)
Padding(padding: const EdgeInsets.only(top: 80), child: LiveTVTab(playlist: widget.playlist)), Container(
Padding(padding: const EdgeInsets.only(top: 80), child: MoviesTab(playlist: widget.playlist)), decoration: const BoxDecoration(
Padding(padding: const EdgeInsets.only(top: 80), child: SeriesTab(playlist: widget.playlist)), gradient: RadialGradient(
const Padding(padding: EdgeInsets.only(top: 80), child: SettingsTab()), center: Alignment(0, -0.2), // Slightly above center
radius: 1.5,
colors: [
Color(0xFF2C2C2E), // Dark Grey center (Light Source)
Color(0xFF000000), // Pure Black edges
],
stops: [0.0, 1.0],
),
),
),
// Content
TabBarView(
controller: _tabController,
physics: const NeverScrollableScrollPhysics(), // Disable swipe to avoid gesture conflicts
children: [
// Padding top to account for the transparent AppBar
Padding(padding: const EdgeInsets.only(top: 80), child: LiveTVTab(playlist: widget.playlist)),
Padding(padding: const EdgeInsets.only(top: 80), child: MoviesTab(playlist: widget.playlist)),
Padding(padding: const EdgeInsets.only(top: 80), child: SeriesTab(playlist: widget.playlist)),
const Padding(padding: EdgeInsets.only(top: 80), child: SettingsTab()),
],
),
], ],
), ),
); );
+6 -2
View File
@@ -52,6 +52,11 @@ class _ChannelCardState extends ConsumerState<ChannelCard>
vsync: this, vsync: this,
duration: const Duration(milliseconds: 1000) duration: const Duration(milliseconds: 1000)
)..repeat(reverse: true); )..repeat(reverse: true);
// Fetch EPG immediately when card is built
WidgetsBinding.instance.addPostFrameCallback((_) {
_fetchEpg();
});
} }
@override @override
@@ -107,7 +112,6 @@ class _ChannelCardState extends ConsumerState<ChannelCard>
return MouseRegion( return MouseRegion(
onEnter: (_) { onEnter: (_) {
setState(() => _isHovered = true); setState(() => _isHovered = true);
if (!_epgLoaded) _fetchEpg();
}, },
onExit: (_) => setState(() => _isHovered = false), onExit: (_) => setState(() => _isHovered = false),
cursor: SystemMouseCursors.click, cursor: SystemMouseCursors.click,
@@ -228,7 +232,7 @@ class _ChannelCardState extends ConsumerState<ChannelCard>
fontSize: 13, fontSize: 13,
), ),
), ),
if ((widget.currentProgram != null || _epgNow != null) && _isHovered) ...[ if ((widget.currentProgram != null || _epgNow != null)) ...[
const SizedBox(height: 2), const SizedBox(height: 2),
Text( Text(
_epgNow ?? widget.currentProgram ?? '', _epgNow ?? widget.currentProgram ?? '',