From e281ba3922f405c95cf01b741c0c4ca4402183d2 Mon Sep 17 00:00:00 2001 From: renato97 Date: Wed, 25 Feb 2026 15:43:24 -0300 Subject: [PATCH] v2: TV optimized UI, country filter fix, landscape mode --- lib/main.dart | 7 + lib/screens/home_screen.dart | 506 ++++++++++++++++++++++------------- 2 files changed, 321 insertions(+), 192 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 3737078..7e0e7f9 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,10 +1,17 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import 'services/iptv_provider.dart'; import 'screens/login_screen.dart'; import 'screens/home_screen.dart'; void main() { + WidgetsFlutterBinding.ensureInitialized(); + SystemChrome.setPreferredOrientations([ + DeviceOrientation.landscapeLeft, + DeviceOrientation.landscapeRight, + ]); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); runApp(const XStreamTVApp()); } diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index c5b40df..0d25c6c 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -13,6 +13,8 @@ class HomeScreen extends StatefulWidget { } class _HomeScreenState extends State { + int _focusedIndex = 0; + @override void initState() { super.initState(); @@ -21,6 +23,20 @@ class _HomeScreenState extends State { }); } + double get _screenWidth => MediaQuery.of(context).size.width; + bool get _isLargeScreen => _screenWidth > 900; + bool get _isMediumScreen => _screenWidth > 600 && _screenWidth <= 900; + + int get _gridCrossAxisCount { + if (_screenWidth > 900) return 6; + if (_screenWidth > 600) return 4; + return 3; + } + + double get _titleFontSize => _isLargeScreen ? 32 : (_isMediumScreen ? 28 : 24); + double get _iconSize => _isLargeScreen ? 80 : 60; + double get _headerPadding => _isLargeScreen ? 32 : 24; + Future _loadInitialData() async { final provider = context.read(); await provider.loadLiveStreams(); @@ -91,20 +107,22 @@ class _HomeScreenState extends State { } Widget _buildHeader(String timeStr, String dateStr) { + final double titleSize = _isLargeScreen ? 28.0 : 24.0; + final double iconSize = _isLargeScreen ? 40.0 : 32.0; return Padding( - padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), + padding: EdgeInsets.symmetric(horizontal: _headerPadding, vertical: _isLargeScreen ? 24 : 16), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Row( + Row( children: [ - Icon(Icons.live_tv, color: Colors.red, size: 32), - SizedBox(width: 12), + Icon(Icons.live_tv, color: Colors.red, size: iconSize), + const SizedBox(width: 12), Text( 'XSTREAM TV', style: TextStyle( color: Colors.white, - fontSize: 24, + fontSize: titleSize, fontWeight: FontWeight.bold, letterSpacing: 2, ), @@ -113,14 +131,14 @@ class _HomeScreenState extends State { ), Row( children: [ - Text(timeStr, style: const TextStyle(color: Colors.white70, fontSize: 16)), + Text(timeStr, style: TextStyle(color: Colors.white70, fontSize: _isLargeScreen ? 20 : 16)), const SizedBox(width: 16), - Text(dateStr, style: const TextStyle(color: Colors.white54, fontSize: 14)), + Text(dateStr, style: TextStyle(color: Colors.white54, fontSize: _isLargeScreen ? 16 : 14)), const SizedBox(width: 24), - const Icon(Icons.person, color: Colors.white70, size: 24), + Icon(Icons.person, color: Colors.white70, size: _isLargeScreen ? 32 : 24), const SizedBox(width: 16), IconButton( - icon: const Icon(Icons.settings, color: Colors.white70), + icon: Icon(Icons.settings, color: Colors.white70, size: _isLargeScreen ? 32 : 24), onPressed: () { context.read().logout(); }, @@ -133,50 +151,63 @@ class _HomeScreenState extends State { } Widget _buildDashboard() { + final double cardSpacing = _isLargeScreen ? 24.0 : 16.0; return Padding( - padding: const EdgeInsets.symmetric(horizontal: 24), + padding: EdgeInsets.symmetric(horizontal: _headerPadding), child: Row( children: [ Expanded( flex: 2, - child: _DashboardCard( - title: 'LIVE TV', - icon: Icons.tv, - gradient: const LinearGradient( - colors: [Color(0xFF00c853), Color(0xFF2979ff)], - begin: Alignment.topLeft, - end: Alignment.bottomRight, + child: Focus( + autofocus: _focusedIndex == 0, + child: _DashboardCard( + title: 'LIVE TV', + icon: Icons.tv, + isLarge: _isLargeScreen, + gradient: const LinearGradient( + colors: [Color(0xFF00c853), Color(0xFF2979ff)], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + onTap: _showLiveCategories, ), - onTap: _showLiveCategories, ), ), - const SizedBox(width: 16), + SizedBox(width: cardSpacing), Expanded( child: Column( children: [ Expanded( - child: _DashboardCard( - title: 'MOVIES', - icon: Icons.play_circle_fill, - gradient: const LinearGradient( - colors: [Color(0xFFff5252), Color(0xFFff9800)], - begin: Alignment.topLeft, - end: Alignment.bottomRight, + child: Focus( + autofocus: _focusedIndex == 1, + child: _DashboardCard( + title: 'MOVIES', + icon: Icons.play_circle_fill, + isLarge: _isLargeScreen, + gradient: const LinearGradient( + colors: [Color(0xFFff5252), Color(0xFFff9800)], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + onTap: _showMovies, ), - onTap: _showMovies, ), ), - const SizedBox(height: 16), + SizedBox(height: cardSpacing), Expanded( - child: _DashboardCard( - title: 'SERIES', - icon: Icons.movie, - gradient: const LinearGradient( - colors: [Color(0xFF9c27b0), Color(0xFF03a9f4)], - begin: Alignment.topLeft, - end: Alignment.bottomRight, + child: Focus( + autofocus: _focusedIndex == 2, + child: _DashboardCard( + title: 'SERIES', + icon: Icons.movie, + isLarge: _isLargeScreen, + gradient: const LinearGradient( + colors: [Color(0xFF9c27b0), Color(0xFF03a9f4)], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + onTap: _showSeries, ), - onTap: _showSeries, ), ), ], @@ -188,27 +219,29 @@ class _HomeScreenState extends State { } Widget _buildFooter() { + final double footerPadding = _isLargeScreen ? 32.0 : 24.0; + final double fontSize = _isLargeScreen ? 16.0 : 12.0; return Consumer( builder: (context, provider, _) { final expDate = provider.userInfo?.expDate; final username = provider.userInfo?.username ?? 'Usuario'; return Padding( - padding: const EdgeInsets.all(24), + padding: EdgeInsets.all(footerPadding), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Expiración: ${_formatExpiry(expDate)}', - style: const TextStyle(color: Colors.white38, fontSize: 12), + style: TextStyle(color: Colors.white38, fontSize: fontSize), ), - const Text( + Text( 'Términos de Servicio', - style: TextStyle(color: Colors.white38, fontSize: 12), + style: TextStyle(color: Colors.white38, fontSize: fontSize), ), Text( 'Usuario: $username', - style: const TextStyle(color: Colors.white38, fontSize: 12), + style: TextStyle(color: Colors.white38, fontSize: fontSize), ), ], ), @@ -223,61 +256,77 @@ class _DashboardCard extends StatelessWidget { final IconData icon; final Gradient gradient; final VoidCallback onTap; + final bool isLarge; const _DashboardCard({ required this.title, required this.icon, required this.gradient, required this.onTap, + this.isLarge = false, }); @override Widget build(BuildContext context) { - return GestureDetector( - onTap: onTap, - child: Container( - decoration: BoxDecoration( - gradient: gradient, - borderRadius: BorderRadius.circular(20), - boxShadow: [ - BoxShadow( - color: Colors.black.withValues(alpha: 0.3), - blurRadius: 15, - offset: const Offset(0, 8), - ), - ], - ), - child: Stack( - children: [ - Positioned( - right: -20, - bottom: -20, - child: Icon( - icon, - size: 150, - color: Colors.white.withValues(alpha: 0.1), + final iconSize = isLarge ? 80.0 : 60.0; + final titleSize = isLarge ? 32.0 : 24.0; + final bgIconSize = isLarge ? 200.0 : 150.0; + return Focus( + child: Builder( + builder: (context) { + final hasFocus = Focus.of(context).hasFocus; + return GestureDetector( + onTap: onTap, + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + decoration: BoxDecoration( + gradient: gradient, + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: hasFocus ? 0.5 : 0.3), + blurRadius: hasFocus ? 25 : 15, + offset: const Offset(0, 8), + ), + ], + border: hasFocus + ? Border.all(color: Colors.white.withValues(alpha: 0.5), width: 3) + : null, ), - ), - Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, + child: Stack( children: [ - Icon(icon, size: 60, color: Colors.white), - const SizedBox(height: 12), - Text( - title, - style: const TextStyle( - color: Colors.white, - fontSize: 24, - fontWeight: FontWeight.bold, - letterSpacing: 2, + Positioned( + right: -40, + bottom: -40, + child: Icon( + icon, + size: bgIconSize, + color: Colors.white.withValues(alpha: 0.1), + ), + ), + Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(icon, size: iconSize, color: Colors.white), + const SizedBox(height: 16), + Text( + title, + style: TextStyle( + color: Colors.white, + fontSize: titleSize, + fontWeight: FontWeight.bold, + letterSpacing: 2, + ), + ), + ], ), ), ], ), ), - ], - ), + ); + }, ), ); } @@ -298,6 +347,7 @@ class _ContentListScreenState extends State { final TextEditingController _searchController = TextEditingController(); String _searchQuery = ''; String? _selectedCountry; + final FocusNode _gridFocusNode = FocusNode(); @override void initState() { @@ -305,6 +355,20 @@ class _ContentListScreenState extends State { _loadContent(); } + double get _screenWidth => MediaQuery.of(context).size.width; + bool get _isLargeScreen => _screenWidth > 900; + bool get _isMediumScreen => _screenWidth > 600 && _screenWidth <= 900; + + int get _gridCrossAxisCount { + if (_screenWidth > 900) return 6; + if (_screenWidth > 600) return 4; + return 3; + } + + double get _titleFontSize => _isLargeScreen ? 32 : (_isMediumScreen ? 28 : 24); + double get _cardTextSize => _isLargeScreen ? 16 : 12; + double get _headerPadding => _isLargeScreen ? 32 : 16; + void _loadContent() { final provider = context.read(); if (widget.type == ContentType.live) { @@ -319,6 +383,7 @@ class _ContentListScreenState extends State { @override void dispose() { _searchController.dispose(); + _gridFocusNode.dispose(); super.dispose(); } @@ -362,37 +427,42 @@ class _ContentListScreenState extends State { } Widget _buildHeader() { + final searchWidth = _isLargeScreen ? 350.0 : (_isMediumScreen ? 300.0 : 250.0); + final searchHeight = _isLargeScreen ? 56.0 : 44.0; + final iconSize = _isLargeScreen ? 32.0 : 24.0; return Container( - padding: const EdgeInsets.all(16), + padding: EdgeInsets.all(_headerPadding), child: Row( children: [ IconButton( - icon: const Icon(Icons.arrow_back, color: Colors.white), + icon: Icon(Icons.arrow_back, color: Colors.white, size: iconSize), onPressed: () => Navigator.pop(context), + iconSize: 48, + padding: EdgeInsets.all(_isLargeScreen ? 12 : 8), ), const SizedBox(width: 8), Text( _title, - style: const TextStyle( + style: TextStyle( color: Colors.white, - fontSize: 24, + fontSize: _titleFontSize, fontWeight: FontWeight.bold, ), ), const Spacer(), SizedBox( - width: 250, - height: 44, + width: searchWidth, + height: searchHeight, child: TextField( controller: _searchController, - style: const TextStyle(color: Colors.white), + style: TextStyle(color: Colors.white, fontSize: _isLargeScreen ? 18 : 14), decoration: InputDecoration( hintText: 'Buscar...', - hintStyle: const TextStyle(color: Colors.grey), - prefixIcon: const Icon(Icons.search, color: Colors.grey), + hintStyle: TextStyle(color: Colors.grey, fontSize: _isLargeScreen ? 18 : 14), + prefixIcon: Icon(Icons.search, color: Colors.grey, size: _isLargeScreen ? 28 : 20), suffixIcon: _searchQuery.isNotEmpty ? IconButton( - icon: const Icon(Icons.clear, color: Colors.grey), + icon: Icon(Icons.clear, color: Colors.grey, size: _isLargeScreen ? 28 : 20), onPressed: () { _searchController.clear(); setState(() => _searchQuery = ''); @@ -405,7 +475,7 @@ class _ContentListScreenState extends State { borderRadius: BorderRadius.circular(8), borderSide: BorderSide.none, ), - contentPadding: const EdgeInsets.symmetric(horizontal: 16), + contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: _isLargeScreen ? 16 : 12), ), onChanged: (value) { setState(() => _searchQuery = value); @@ -417,6 +487,13 @@ class _ContentListScreenState extends State { ); } + String _getCountryName(String categoryName) { + if (categoryName.contains('|')) { + return categoryName.split('|').first.trim(); + } + return categoryName.trim(); + } + Widget _buildCountryFilter() { if (widget.type != ContentType.live) { return const SizedBox.shrink(); @@ -427,24 +504,27 @@ class _ContentListScreenState extends State { return const SizedBox.shrink(); } - final countries = categories.map((c) => c.name).toList(); + final countries = categories.map((c) => _getCountryName(c.name)).toList(); + final chipHeight = _isLargeScreen ? 56.0 : 50.0; + final chipFontSize = _isLargeScreen ? 16.0 : 14.0; return Container( - height: 50, - margin: const EdgeInsets.only(bottom: 8), + height: chipHeight, + margin: EdgeInsets.only(bottom: _isLargeScreen ? 16 : 8), child: ListView.builder( scrollDirection: Axis.horizontal, - padding: const EdgeInsets.symmetric(horizontal: 16), + padding: EdgeInsets.symmetric(horizontal: _headerPadding), itemCount: countries.length + 1, itemBuilder: (context, index) { if (index == 0) { return Padding( - padding: const EdgeInsets.only(right: 8), + padding: EdgeInsets.only(right: _isLargeScreen ? 12 : 8), child: FilterChip( - label: const Text('Todos', style: TextStyle(color: Colors.white)), + label: Text('Todos', style: TextStyle(color: Colors.white, fontSize: chipFontSize)), selected: _selectedCountry == null, selectedColor: Colors.red, backgroundColor: Colors.grey[800], + padding: EdgeInsets.symmetric(horizontal: _isLargeScreen ? 12 : 8), onSelected: (_) { setState(() => _selectedCountry = null); context.read().loadLiveStreams(''); @@ -453,20 +533,22 @@ class _ContentListScreenState extends State { ); } - final country = countries[index - 1]; + final countryName = countries[index - 1]; + final category = categories[index - 1]; return Padding( - padding: const EdgeInsets.only(right: 8), + padding: EdgeInsets.only(right: _isLargeScreen ? 12 : 8), child: FilterChip( label: Text( - country.length > 20 ? '${country.substring(0, 20)}...' : country, - style: const TextStyle(color: Colors.white), + countryName.length > 20 ? '${countryName.substring(0, 20)}...' : countryName, + style: TextStyle(color: Colors.white, fontSize: chipFontSize), ), - selected: _selectedCountry == categories[index - 1].id, + selected: _selectedCountry == category.id, selectedColor: Colors.red, backgroundColor: Colors.grey[800], + padding: EdgeInsets.symmetric(horizontal: _isLargeScreen ? 12 : 8), onSelected: (_) { - setState(() => _selectedCountry = categories[index - 1].id); - context.read().loadLiveStreams(categories[index - 1].id); + setState(() => _selectedCountry = category.id); + context.read().loadLiveStreams(category.id); }, ), ); @@ -476,10 +558,12 @@ class _ContentListScreenState extends State { } Widget _buildContentList() { + final padding = _isLargeScreen ? 24.0 : 16.0; + final spacing = _isLargeScreen ? 16.0 : 12.0; return Consumer( builder: (context, provider, _) { if (provider.isLoading) { - return const Center(child: CircularProgressIndicator(color: Colors.red)); + return Center(child: CircularProgressIndicator(color: Colors.red, strokeWidth: _isLargeScreen ? 4 : 2)); } List streams = []; @@ -507,18 +591,18 @@ class _ContentListScreenState extends State { return Center( child: Text( _searchQuery.isNotEmpty ? 'No se encontraron resultados' : 'Sin contenido', - style: const TextStyle(color: Colors.grey, fontSize: 16), + style: TextStyle(color: Colors.grey, fontSize: _isLargeScreen ? 20 : 16), ), ); } return GridView.builder( - padding: const EdgeInsets.all(16), - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 4, + padding: EdgeInsets.all(padding), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: _gridCrossAxisCount, childAspectRatio: 16 / 9, - crossAxisSpacing: 12, - mainAxisSpacing: 12, + crossAxisSpacing: spacing, + mainAxisSpacing: spacing, ), itemCount: streams.length, itemBuilder: (context, index) { @@ -526,6 +610,7 @@ class _ContentListScreenState extends State { return _ChannelCard( stream: stream, isSeries: widget.type == ContentType.series, + isLarge: _isLargeScreen, onTap: () { if (widget.type == ContentType.series) { final series = provider.seriesList.firstWhere( @@ -561,100 +646,128 @@ class _ChannelCard extends StatelessWidget { final XtreamStream stream; final bool isSeries; final VoidCallback onTap; + final bool isLarge; const _ChannelCard({ required this.stream, required this.isSeries, required this.onTap, + this.isLarge = false, }); @override Widget build(BuildContext context) { - return GestureDetector( - onTap: onTap, - child: Container( - decoration: BoxDecoration( - color: Colors.grey[900], - borderRadius: BorderRadius.circular(12), - border: Border.all(color: Colors.red.withValues(alpha: 0.3)), - ), - child: Stack( - children: [ - if (stream.streamIcon != null && stream.streamIcon!.isNotEmpty) - ClipRRect( - borderRadius: BorderRadius.circular(12), - child: Image.network( - stream.streamIcon!, - width: double.infinity, - height: double.infinity, - fit: BoxFit.cover, - errorBuilder: (_, __, ___) => _buildPlaceholder(), - ), - ) - else - _buildPlaceholder(), - Container( + final textSize = isLarge ? 16.0 : 12.0; + final ratingFontSize = isLarge ? 14.0 : 10.0; + final placeholderIconSize = isLarge ? 56.0 : 40.0; + final padding = isLarge ? 12.0 : 8.0; + final ratingPaddingH = isLarge ? 10.0 : 6.0; + final ratingPaddingV = isLarge ? 4.0 : 2.0; + return Focus( + child: Builder( + builder: (context) { + final hasFocus = Focus.of(context).hasFocus; + return GestureDetector( + onTap: onTap, + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - Colors.transparent, - Colors.black.withValues(alpha: 0.8), - ], + color: Colors.grey[900], + borderRadius: BorderRadius.circular(16), + border: Border.all( + color: hasFocus ? Colors.red : Colors.red.withValues(alpha: 0.3), + width: hasFocus ? 3 : 1, ), + boxShadow: hasFocus + ? [ + BoxShadow( + color: Colors.red.withValues(alpha: 0.3), + blurRadius: 15, + spreadRadius: 2, + ), + ] + : null, ), - ), - Positioned( - bottom: 8, - left: 8, - right: 8, - child: Text( - stream.name, - style: const TextStyle( - color: Colors.white, - fontSize: 12, - fontWeight: FontWeight.w500, - ), - maxLines: 2, - overflow: TextOverflow.ellipsis, - ), - ), - if (stream.rating != null) - Positioned( - top: 8, - right: 8, - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), - decoration: BoxDecoration( - color: Colors.amber, - borderRadius: BorderRadius.circular(4), - ), - child: Text( - stream.rating!, - style: const TextStyle( - color: Colors.black, - fontSize: 10, - fontWeight: FontWeight.bold, + child: Stack( + children: [ + if (stream.streamIcon != null && stream.streamIcon!.isNotEmpty) + ClipRRect( + borderRadius: BorderRadius.circular(16), + child: Image.network( + stream.streamIcon!, + width: double.infinity, + height: double.infinity, + fit: BoxFit.cover, + errorBuilder: (_, __, ___) => _buildPlaceholder(placeholderIconSize), + ), + ) + else + _buildPlaceholder(placeholderIconSize), + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.transparent, + Colors.black.withValues(alpha: 0.8), + ], + ), ), ), - ), + Positioned( + bottom: padding, + left: padding, + right: padding, + child: Text( + stream.name, + style: TextStyle( + color: Colors.white, + fontSize: textSize, + fontWeight: FontWeight.w500, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ), + if (stream.rating != null) + Positioned( + top: padding, + right: padding, + child: Container( + padding: EdgeInsets.symmetric(horizontal: ratingPaddingH, vertical: ratingPaddingV), + decoration: BoxDecoration( + color: Colors.amber, + borderRadius: BorderRadius.circular(6), + ), + child: Text( + stream.rating!, + style: TextStyle( + color: Colors.black, + fontSize: ratingFontSize, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], ), - ], - ), + ), + ); + }, ), ); } - Widget _buildPlaceholder() { + Widget _buildPlaceholder(double iconSize) { return Container( color: Colors.grey[800], child: Center( child: Icon( isSeries ? Icons.tv : Icons.play_circle_outline, color: Colors.red, - size: 40, + size: iconSize, ), ), ); @@ -679,28 +792,36 @@ class _SeriesEpisodesScreenState extends State { }); } + double get _screenWidth => MediaQuery.of(context).size.width; + bool get _isLargeScreen => _screenWidth > 900; + @override Widget build(BuildContext context) { + final double fontSize = _isLargeScreen ? 24.0 : 20.0; + final double iconSize = _isLargeScreen ? 56.0 : 40.0; + final double padding = _isLargeScreen ? 24.0 : 16.0; return Scaffold( backgroundColor: const Color(0xFF0f0f1a), body: SafeArea( child: Column( children: [ Container( - padding: const EdgeInsets.all(16), + padding: EdgeInsets.all(padding), child: Row( children: [ IconButton( - icon: const Icon(Icons.arrow_back, color: Colors.white), + icon: Icon(Icons.arrow_back, color: Colors.white, size: iconSize), onPressed: () => Navigator.pop(context), + iconSize: 48, + padding: EdgeInsets.all(_isLargeScreen ? 12 : 8), ), const SizedBox(width: 8), Expanded( child: Text( widget.series.name, - style: const TextStyle( + style: TextStyle( color: Colors.white, - fontSize: 20, + fontSize: fontSize, fontWeight: FontWeight.bold, ), maxLines: 1, @@ -714,38 +835,39 @@ class _SeriesEpisodesScreenState extends State { child: Consumer( builder: (context, provider, _) { if (provider.isLoading) { - return const Center( - child: CircularProgressIndicator(color: Colors.red), + return Center( + child: CircularProgressIndicator(color: Colors.red, strokeWidth: _isLargeScreen ? 4 : 2), ); } final episodes = provider.seriesEpisodes; if (episodes.isEmpty) { - return const Center( + return Center( child: Text( 'No hay episodios', - style: TextStyle(color: Colors.grey), + style: TextStyle(color: Colors.grey, fontSize: _isLargeScreen ? 20 : 16), ), ); } return ListView.builder( - padding: const EdgeInsets.all(16), + padding: EdgeInsets.all(padding), itemCount: episodes.length, itemBuilder: (context, index) { final episode = episodes[index]; return Card( color: Colors.grey[900], - margin: const EdgeInsets.only(bottom: 8), + margin: EdgeInsets.only(bottom: _isLargeScreen ? 16 : 8), child: ListTile( - leading: const Icon( + contentPadding: EdgeInsets.all(_isLargeScreen ? 16 : 12), + leading: Icon( Icons.play_circle_fill, color: Colors.red, - size: 40, + size: iconSize, ), title: Text( 'S${episode.seasonNumber}E${episode.episodeNumber} - ${episode.title}', - style: const TextStyle(color: Colors.white), + style: TextStyle(color: Colors.white, fontSize: _isLargeScreen ? 20 : 16), ), onTap: () { Navigator.push(