From eace76b3a61d42b6d03ae105bd6269bd6ee49ead Mon Sep 17 00:00:00 2001 From: sohailaahmed12 Date: Sat, 2 May 2026 18:03:21 +0300 Subject: [PATCH 1/2] -added track filtering in following feed --- lib/features/feed/data/datasources/feed_remote_datasource.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/features/feed/data/datasources/feed_remote_datasource.dart b/lib/features/feed/data/datasources/feed_remote_datasource.dart index 795d153e..74b83928 100644 --- a/lib/features/feed/data/datasources/feed_remote_datasource.dart +++ b/lib/features/feed/data/datasources/feed_remote_datasource.dart @@ -49,6 +49,7 @@ class FeedRemoteDatasourceImpl implements FeedDatasource { return data .map((e) => _parseItem(e as Map)) .whereType() + .where((item) => item.contentType == 'track') .toList(); } From ea9d367e424efc29bc92ee1641288a7f6d8944b0 Mon Sep 17 00:00:00 2001 From: sohailaahmed12 Date: Sat, 2 May 2026 19:21:31 +0300 Subject: [PATCH 2/2] -added track filtering in feed -added music note icon fallback in all modules --- .../widgets/discover_with_stations.dart | 20 ++++++-- .../presentation/widgets/hot_for_you.dart | 14 ++++- .../presentation/widgets/mixed_for_you.dart | 48 +++++++++++++---- .../widgets/more_of_what_you_like.dart | 48 +++++++++++++---- .../widgets/trending_by_genre.dart | 10 ++++ .../widgets/search_albums_tab.dart | 14 ++++- .../presentation/widgets/search_all_tab.dart | 51 +++++++++++++++---- .../widgets/search_playlists_tab.dart | 17 +++++-- .../presentation/widgets/track_tile.dart | 24 +++++++-- .../widgets/vibes_genre_albums.dart | 26 ++++++++-- .../widgets/vibes_genre_all_tab.dart | 20 ++++++++ .../widgets/vibes_genre_introducing.dart | 24 ++++++++- .../widgets/vibes_genre_playlists.dart | 26 ++++++++-- 13 files changed, 290 insertions(+), 52 deletions(-) diff --git a/lib/features/feed/presentation/widgets/discover_with_stations.dart b/lib/features/feed/presentation/widgets/discover_with_stations.dart index f985b0e2..9c251893 100644 --- a/lib/features/feed/presentation/widgets/discover_with_stations.dart +++ b/lib/features/feed/presentation/widgets/discover_with_stations.dart @@ -350,9 +350,23 @@ class _CircleAvatar extends StatelessWidget { shape: BoxShape.circle, border: Border.all(color: borderColor, width: borderWidth), color: Colors.grey[900], - image: imageUrl.startsWith('http') - ? DecorationImage(image: NetworkImage(imageUrl), fit: BoxFit.cover) - : null, + ), + child: ClipOval( + child: imageUrl.startsWith('http') + ? Image.network( + imageUrl, + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) => const Center( + child: Icon( + Icons.music_note, + color: Colors.white54, + size: 20, + ), + ), + ) + : const Center( + child: Icon(Icons.music_note, color: Colors.white54, size: 20), + ), ), ); } diff --git a/lib/features/feed/presentation/widgets/hot_for_you.dart b/lib/features/feed/presentation/widgets/hot_for_you.dart index 51df0e40..3d6185f2 100644 --- a/lib/features/feed/presentation/widgets/hot_for_you.dart +++ b/lib/features/feed/presentation/widgets/hot_for_you.dart @@ -265,6 +265,7 @@ class _HotForYouCardState extends ConsumerState height: 22, decoration: BoxDecoration( shape: BoxShape.circle, + color: Colors.grey[800], image: DecorationImage( image: widget.track.coverImage != null && @@ -299,7 +300,18 @@ class _HotForYouCardState extends ConsumerState child: widget.track.coverImage != null && widget.track.coverImage!.startsWith('http') - ? Image.network(widget.track.coverImage!, fit: BoxFit.cover) + ? Image.network( + widget.track.coverImage!, + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) => + const Center( + child: Icon( + Icons.music_note, + color: Colors.white54, + size: 28, + ), + ), + ) : Image.asset( widget.track.coverImage ?? widget.track.artworkUrl, fit: BoxFit.cover, diff --git a/lib/features/feed/presentation/widgets/mixed_for_you.dart b/lib/features/feed/presentation/widgets/mixed_for_you.dart index 9e061fb3..b00f1f18 100644 --- a/lib/features/feed/presentation/widgets/mixed_for_you.dart +++ b/lib/features/feed/presentation/widgets/mixed_for_you.dart @@ -120,18 +120,48 @@ class MixedPlaylistCard extends ConsumerWidget { decoration: BoxDecoration( borderRadius: BorderRadius.circular(1), border: Border.all(color: Colors.grey, width: 0.5), - image: imagePath.isNotEmpty - ? DecorationImage( - image: imagePath.startsWith('http') - ? NetworkImage(imagePath) as ImageProvider - : AssetImage(imagePath), + color: Colors.grey[800], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(1), + child: imagePath.isEmpty + ? const Center( + child: Icon( + Icons.music_note, + color: Colors.white54, + size: 32, + ), + ) + : imagePath.startsWith('http') + ? Image.network( + imagePath, fit: BoxFit.cover, + width: double.infinity, + height: 130, + errorBuilder: (context, error, stackTrace) => + const Center( + child: Icon( + Icons.music_note, + color: Colors.white54, + size: 32, + ), + ), ) - : null, + : Image.asset( + imagePath, + fit: BoxFit.cover, + width: double.infinity, + height: 130, + errorBuilder: (context, error, stackTrace) => + const Center( + child: Icon( + Icons.music_note, + color: Colors.white54, + size: 32, + ), + ), + ), ), - child: imagePath.isEmpty - ? const Center(child: Icon(Icons.music_note)) - : null, ), Positioned( bottom: 8, diff --git a/lib/features/feed/presentation/widgets/more_of_what_you_like.dart b/lib/features/feed/presentation/widgets/more_of_what_you_like.dart index 216f8525..006529e5 100644 --- a/lib/features/feed/presentation/widgets/more_of_what_you_like.dart +++ b/lib/features/feed/presentation/widgets/more_of_what_you_like.dart @@ -112,18 +112,48 @@ class PlaylistSquareCard extends StatelessWidget { decoration: BoxDecoration( borderRadius: BorderRadius.circular(1), border: Border.all(color: const Color(0xFF6C7C71), width: 0.5), - image: imagePath.isNotEmpty - ? DecorationImage( - image: imagePath.startsWith('http') - ? NetworkImage(imagePath) as ImageProvider - : AssetImage(imagePath), + color: Colors.grey[800], // fallback background always present + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(1), + child: imagePath.isEmpty + ? const Center( + child: Icon( + Icons.music_note, + color: Colors.white54, + size: 32, + ), + ) + : imagePath.startsWith('http') + ? Image.network( + imagePath, fit: BoxFit.cover, + width: double.infinity, + height: 140, + errorBuilder: (context, error, stackTrace) => + const Center( + child: Icon( + Icons.music_note, + color: Colors.white54, + size: 32, + ), + ), ) - : null, + : Image.asset( + imagePath, + fit: BoxFit.cover, + width: double.infinity, + height: 140, + errorBuilder: (context, error, stackTrace) => + const Center( + child: Icon( + Icons.music_note, + color: Colors.white54, + size: 32, + ), + ), + ), ), - child: imagePath.isEmpty - ? const Center(child: Icon(Icons.playlist_play)) - : null, ), const SizedBox(height: 8), Text( diff --git a/lib/features/feed/presentation/widgets/trending_by_genre.dart b/lib/features/feed/presentation/widgets/trending_by_genre.dart index a06bf627..53147c95 100644 --- a/lib/features/feed/presentation/widgets/trending_by_genre.dart +++ b/lib/features/feed/presentation/widgets/trending_by_genre.dart @@ -357,12 +357,22 @@ class _TrendingHorizontalColumns extends ConsumerWidget { width: 50, height: 50, color: Colors.grey[800], + child: const Icon( + Icons.music_note, + color: Colors.white54, + size: 22, + ), ), ) : Container( width: 50, height: 50, color: Colors.grey[800], + child: const Icon( + Icons.music_note, + color: Colors.white54, + size: 22, + ), ), ), ), diff --git a/lib/features/search/presentation/widgets/search_albums_tab.dart b/lib/features/search/presentation/widgets/search_albums_tab.dart index 50254b45..330376f7 100644 --- a/lib/features/search/presentation/widgets/search_albums_tab.dart +++ b/lib/features/search/presentation/widgets/search_albums_tab.dart @@ -66,8 +66,18 @@ class _AlbumTile extends StatelessWidget { width: 50, height: 50, fit: BoxFit.cover, - errorBuilder: (_, _, _) => - Container(width: 50, height: 50, color: Colors.grey[800]), + errorBuilder: (_, _, _) => Container( + width: 50, + height: 50, + color: Colors.grey[800], + child: const Center( + child: Icon( + Icons.music_note, + color: Colors.white54, + size: 32, + ), + ), + ), ), ), title: Text( diff --git a/lib/features/search/presentation/widgets/search_all_tab.dart b/lib/features/search/presentation/widgets/search_all_tab.dart index 5fa15723..0b8bb566 100644 --- a/lib/features/search/presentation/widgets/search_all_tab.dart +++ b/lib/features/search/presentation/widgets/search_all_tab.dart @@ -7,7 +7,6 @@ import '../providers/search_providers.dart'; import '../widgets/track_tile.dart'; import '../widgets/search_profiles_tab.dart'; import '../../../../core/domain/entities/track.dart'; -import '../../../../core/utils/formatters.dart'; import '../pages/search_seeall_page.dart'; import '../widgets/search_tracks_tab.dart'; import '../../../player/presentation/providers/queue_provider.dart'; @@ -203,8 +202,16 @@ class _PlaylistRow extends StatelessWidget { width: 50, height: 50, fit: BoxFit.cover, - errorBuilder: (_, _, _) => - Container(width: 50, height: 50, color: Colors.grey[800]), + errorBuilder: (_, _, _) => Container( + width: 50, + height: 50, + color: Colors.grey[800], + child: const Icon( + Icons.music_note, + color: Colors.white54, + size: 22, + ), + ), ), ), title: Text( @@ -214,7 +221,7 @@ class _PlaylistRow extends StatelessWidget { ), // Duration is formatted from raw seconds via [Formatters.formatPlaylistDuration]. subtitle: Text( - 'Playlist · ${playlist['trackCount']} tracks · ${Formatters.formatPlaylistDuration(int.parse(playlist['totalSeconds'] ?? '0'))}', + 'Playlist · ${playlist['trackCount']} tracks', maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle(color: Colors.grey[400], fontSize: 12), @@ -248,8 +255,16 @@ class _AlbumRow extends StatelessWidget { width: 50, height: 50, fit: BoxFit.cover, - errorBuilder: (_, _, _) => - Container(width: 50, height: 50, color: Colors.grey[800]), + errorBuilder: (_, _, _) => Container( + width: 50, + height: 50, + color: Colors.grey[800], + child: const Icon( + Icons.music_note, + color: Colors.white54, + size: 22, + ), + ), ), ), title: Text( @@ -333,8 +348,16 @@ class _TopResultCard extends ConsumerWidget { width: 50, height: 50, fit: BoxFit.cover, - errorBuilder: (_, _, _) => - Container(width: 50, height: 50, color: Colors.grey[800]), + errorBuilder: (_, _, _) => Container( + width: 50, + height: 50, + color: Colors.grey[800], + child: const Icon( + Icons.music_note, + color: Colors.white54, + size: 22, + ), + ), ), ), title: Text( @@ -360,8 +383,16 @@ class _TopResultCard extends ConsumerWidget { width: 50, height: 50, fit: BoxFit.cover, - errorBuilder: (_, _, _) => - Container(width: 50, height: 50, color: Colors.grey[800]), + errorBuilder: (_, _, _) => Container( + width: 50, + height: 50, + color: Colors.grey[800], + child: const Icon( + Icons.music_note, + color: Colors.white54, + size: 22, + ), + ), ), ), title: Text( diff --git a/lib/features/search/presentation/widgets/search_playlists_tab.dart b/lib/features/search/presentation/widgets/search_playlists_tab.dart index 333bf943..59407527 100644 --- a/lib/features/search/presentation/widgets/search_playlists_tab.dart +++ b/lib/features/search/presentation/widgets/search_playlists_tab.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../providers/search_providers.dart'; -import '../../../../core/utils/formatters.dart'; import 'package:go_router/go_router.dart'; /// Search results tab displaying the playlists list from [searchResultsProvider]. @@ -68,8 +67,18 @@ class _PlaylistTile extends StatelessWidget { width: 50, height: 50, fit: BoxFit.cover, - errorBuilder: (_, _, _) => - Container(width: 50, height: 50, color: Colors.grey[800]), + errorBuilder: (_, _, _) => Container( + width: 50, + height: 50, + color: Colors.grey[800], + child: const Center( + child: Icon( + Icons.music_note, + color: Colors.white54, + size: 32, + ), + ), + ), ), ), title: Text( @@ -78,7 +87,7 @@ class _PlaylistTile extends StatelessWidget { overflow: TextOverflow.ellipsis, ), subtitle: Text( - 'Playlist · ${playlist['trackCount']} tracks · ${Formatters.formatPlaylistDuration(int.parse(playlist['totalSeconds'] ?? '0'))}', + 'Playlist · ${playlist['trackCount']} tracks ', maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle(color: Colors.grey[400], fontSize: 12), diff --git a/lib/features/search/presentation/widgets/track_tile.dart b/lib/features/search/presentation/widgets/track_tile.dart index 081d8d28..7315d84d 100644 --- a/lib/features/search/presentation/widgets/track_tile.dart +++ b/lib/features/search/presentation/widgets/track_tile.dart @@ -27,8 +27,16 @@ class TrackTile extends ConsumerWidget { width: 50, height: 50, fit: BoxFit.cover, - errorBuilder: (_, _, _) => - Container(width: 50, height: 50, color: Colors.grey[800]), + errorBuilder: (_, _, _) => Container( + width: 50, + height: 50, + color: Colors.grey[800], + child: const Icon( + Icons.music_note, + color: Colors.white54, + size: 22, + ), + ), ) : Image.asset( track.artworkUrl.isNotEmpty @@ -37,8 +45,16 @@ class TrackTile extends ConsumerWidget { width: 50, height: 50, fit: BoxFit.cover, - errorBuilder: (_, _, _) => - Container(width: 50, height: 50, color: Colors.grey[800]), + errorBuilder: (_, _, _) => Container( + width: 50, + height: 50, + color: Colors.grey[800], + child: const Icon( + Icons.music_note, + color: Colors.white54, + size: 22, + ), + ), ), ), title: Text(track.title, maxLines: 1, overflow: TextOverflow.ellipsis), diff --git a/lib/features/search/presentation/widgets/vibes_genre_albums.dart b/lib/features/search/presentation/widgets/vibes_genre_albums.dart index 8c3e399f..f3f33fc8 100644 --- a/lib/features/search/presentation/widgets/vibes_genre_albums.dart +++ b/lib/features/search/presentation/widgets/vibes_genre_albums.dart @@ -27,8 +27,17 @@ class GenreAlbumCard extends StatelessWidget { width: double.infinity, height: 160, fit: BoxFit.cover, - errorBuilder: (_, _, _) => - Container(height: 160, color: Colors.grey[800]), + errorBuilder: (_, _, _) => Container( + height: 160, + color: Colors.grey[800], + child: const Center( + child: Icon( + Icons.music_note, + color: Colors.white54, + size: 32, + ), + ), + ), ) : Image.asset( album.coverImage.isNotEmpty @@ -38,8 +47,17 @@ class GenreAlbumCard extends StatelessWidget { width: double.infinity, height: 160, fit: BoxFit.cover, - errorBuilder: (_, _, _) => - Container(height: 160, color: Colors.grey[800]), + errorBuilder: (_, _, _) => Container( + height: 160, + color: Colors.grey[800], + child: const Center( + child: Icon( + Icons.music_note, + color: Colors.white54, + size: 32, + ), + ), + ), ), ), const SizedBox(height: 6), diff --git a/lib/features/search/presentation/widgets/vibes_genre_all_tab.dart b/lib/features/search/presentation/widgets/vibes_genre_all_tab.dart index 4518995c..294cc734 100644 --- a/lib/features/search/presentation/widgets/vibes_genre_all_tab.dart +++ b/lib/features/search/presentation/widgets/vibes_genre_all_tab.dart @@ -264,6 +264,16 @@ class IntroducingWidget extends StatelessWidget { width: 60, height: 60, fit: BoxFit.cover, + errorBuilder: (_, _, _) => Container( + width: 60, + height: 60, + color: Colors.grey[800], + child: const Icon( + Icons.music_note, + color: Colors.white54, + size: 22, + ), + ), ) : Image.asset( playlist.coverImage.isNotEmpty @@ -272,6 +282,16 @@ class IntroducingWidget extends StatelessWidget { width: 60, height: 60, fit: BoxFit.cover, + errorBuilder: (_, _, _) => Container( + width: 60, + height: 60, + color: Colors.grey[800], + child: const Icon( + Icons.music_note, + color: Colors.white54, + size: 22, + ), + ), ), ), const SizedBox(width: 12), diff --git a/lib/features/search/presentation/widgets/vibes_genre_introducing.dart b/lib/features/search/presentation/widgets/vibes_genre_introducing.dart index 43d763c3..6736dfe9 100644 --- a/lib/features/search/presentation/widgets/vibes_genre_introducing.dart +++ b/lib/features/search/presentation/widgets/vibes_genre_introducing.dart @@ -18,10 +18,30 @@ Widget _buildImage( BoxFit fit = BoxFit.cover, }) { if (url.isEmpty) { - return Container(width: width, height: height, color: Colors.grey[800]); + return Container( + width: width, + height: height, + color: Colors.grey[800], + child: const Center( + child: Icon(Icons.music_note, color: Colors.white54, size: 32), + ), + ); } if (url.startsWith('http')) { - return Image.network(url, width: width, height: height, fit: fit); + return Image.network( + url, + width: width, + height: height, + fit: fit, + errorBuilder: (_, _, _) => Container( + width: width, + height: height, + color: Colors.grey[800], + child: const Center( + child: Icon(Icons.music_note, color: Colors.white54, size: 32), + ), + ), + ); } return Image.asset(url, width: width, height: height, fit: fit); } diff --git a/lib/features/search/presentation/widgets/vibes_genre_playlists.dart b/lib/features/search/presentation/widgets/vibes_genre_playlists.dart index 12730245..7ce03647 100644 --- a/lib/features/search/presentation/widgets/vibes_genre_playlists.dart +++ b/lib/features/search/presentation/widgets/vibes_genre_playlists.dart @@ -31,8 +31,17 @@ class GenrePlaylistCard extends StatelessWidget { width: double.infinity, height: 160, fit: BoxFit.cover, - errorBuilder: (_, _, _) => - Container(height: 160, color: Colors.grey[800]), + errorBuilder: (_, _, _) => Container( + height: 160, + color: Colors.grey[800], + child: const Center( + child: Icon( + Icons.music_note, + color: Colors.white54, + size: 32, + ), + ), + ), ) : Image.asset( playlist.coverImage.isNotEmpty @@ -42,8 +51,17 @@ class GenrePlaylistCard extends StatelessWidget { width: double.infinity, height: 160, fit: BoxFit.cover, - errorBuilder: (_, _, _) => - Container(height: 160, color: Colors.grey[800]), + errorBuilder: (_, _, _) => Container( + height: 160, + color: Colors.grey[800], + child: const Center( + child: Icon( + Icons.music_note, + color: Colors.white54, + size: 32, + ), + ), + ), ), ), const SizedBox(height: 6),