/// Artist Provider - Artist details state management library; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../infrastructure/datasources/remote/music_api_service.dart'; import '../../../domain/entities/artist.dart'; import '../../../domain/entities/track.dart'; import '../../../domain/entities/album.dart'; /// Artist state class ArtistState { final Artist? artist; final List topTracks; final List albums; final List relatedTracks; final bool isLoading; final String? error; const ArtistState({ this.artist, this.topTracks = const [], this.albums = const [], this.relatedTracks = const [], this.isLoading = false, this.error, }); ArtistState copyWith({ Artist? artist, List? topTracks, List? albums, List? relatedTracks, bool? isLoading, String? error, }) { return ArtistState( artist: artist ?? this.artist, topTracks: topTracks ?? this.topTracks, albums: albums ?? this.albums, relatedTracks: relatedTracks ?? this.relatedTracks, isLoading: isLoading ?? this.isLoading, error: error, ); } } /// Artist notifier class ArtistNotifier extends StateNotifier { ArtistNotifier(this._musicApiService) : super(const ArtistState()); final MusicApiService _musicApiService; /// Load complete artist information Future loadArtist(String artistId) async { state = state.copyWith(isLoading: true, error: null); try { // Load artist details final artistData = await _musicApiService.getArtist(artistId); final artist = Artist.fromJson(artistData); state = state.copyWith( artist: artist, isLoading: false, ); } catch (e) { state = state.copyWith( isLoading: false, error: e.toString(), ); } } /// Load artist's top tracks Future loadTopTracks(String artistId) async { try { final tracksData = await _musicApiService.getArtistTopTracks(artistId); final tracks = tracksData .map((json) => Track.fromJson(json as Map)) .toList(); state = state.copyWith(topTracks: tracks); } catch (e) { state = state.copyWith(error: e.toString()); } } /// Load artist's albums Future loadAlbums(String artistId) async { try { final albumsData = await _musicApiService.getArtistAlbums(artistId); final albums = albumsData .map((json) => Album.fromJson(json as Map)) .toList(); state = state.copyWith(albums: albums); } catch (e) { state = state.copyWith(error: e.toString()); } } /// Load related tracks (based on artist's top track) Future loadRelatedTracks(String artistId) async { try { // Get first track from top tracks for recommendations if (state.topTracks.isEmpty) { await loadTopTracks(artistId); } if (state.topTracks.isNotEmpty) { final firstTrack = state.topTracks.first; final relatedData = await _musicApiService.getRecommendations(firstTrack.id); final related = relatedData .map((json) => Track.fromJson(json as Map)) .toList(); state = state.copyWith(relatedTracks: related); } } catch (e) { state = state.copyWith(error: e.toString()); } } /// Load all artist data at once Future loadAllArtistData(String artistId) async { state = state.copyWith(isLoading: true, error: null); try { // Load all data in parallel final results = await Future.wait([ _musicApiService.getArtist(artistId), _musicApiService.getArtistTopTracks(artistId), _musicApiService.getArtistAlbums(artistId), ]); final artist = Artist.fromJson(results[0] as Map); final topTracks = (results[1] as List) .map((json) => Track.fromJson(json as Map)) .toList(); final albums = (results[2] as List) .map((json) => Album.fromJson(json as Map)) .toList(); // Load related tracks based on first top track List relatedTracks = []; if (topTracks.isNotEmpty) { try { final relatedData = await _musicApiService.getRecommendations(topTracks.first.id); relatedTracks = relatedData .map((json) => Track.fromJson(json as Map)) .toList(); } catch (_) { // Don't fail if recommendations fail } } state = ArtistState( artist: artist, topTracks: topTracks, albums: albums, relatedTracks: relatedTracks, isLoading: false, ); } catch (e) { state = state.copyWith( isLoading: false, error: e.toString(), ); } } /// Clear state void clear() { state = const ArtistState(); } } /// Artist provider final artistProvider = StateNotifierProvider((ref) { final musicApiService = ref.watch(musicApiServiceProvider); return ArtistNotifier(musicApiService); }); /// Artist data provider for a specific artist ID final artistDataProvider = Provider.family((ref, artistId) { final notifier = ref.watch(artistProvider.notifier); // Load data when first accessed if (notifier.state.artist?.id != artistId) { Future.microtask(() => notifier.loadAllArtistData(artistId)); } return notifier.state; });