diff --git a/lib/logic/audio_handler.dart b/lib/logic/audio_handler.dart index 8abe74d..20687dc 100644 --- a/lib/logic/audio_handler.dart +++ b/lib/logic/audio_handler.dart @@ -1,7 +1,11 @@ +import 'dart:typed_data'; import 'package:audio_service/audio_service.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:http/http.dart' as http; import 'package:media_kit/media_kit.dart' as media_kit; import 'package:groovybox/data/db.dart'; +import 'package:groovybox/logic/metadata_service.dart'; +import 'package:groovybox/providers/audio_provider.dart'; import 'package:groovybox/providers/theme_provider.dart'; import 'package:groovybox/providers/remote_provider.dart'; import 'package:groovybox/providers/db_provider.dart'; @@ -47,11 +51,13 @@ class AudioHandler extends BaseAudioHandler with QueueHandler, SeekHandler { _container = container; } - // Update theme color based on current track's album art + // Update theme color based on current track's album art and set current metadata void _updateThemeFromCurrentTrack(MediaItem mediaItem) async { if (_container == null) return; try { + TrackMetadata? metadata; + // For remote tracks, get metadata from database final urlResolver = _container!.read(remoteUrlResolverProvider); if (urlResolver.isProtocolUrl(mediaItem.id)) { @@ -60,22 +66,59 @@ class AudioHandler extends BaseAudioHandler with QueueHandler, SeekHandler { database.tracks, )..where((t) => t.path.equals(mediaItem.id))).getSingleOrNull(); - if (track != null && track.artUri != null) { + if (track != null) { // Fetch album art bytes for remote tracks - // TODO: Implement remote album art fetching for theme + Uint8List? artBytes; + if (track.artUri != null) { + try { + final response = await http.get(Uri.parse(track.artUri!)); + if (response.statusCode == 200) { + artBytes = response.bodyBytes; + } + } catch (e) { + // Ignore art fetching errors + } + } + + metadata = TrackMetadata( + title: track.title, + artist: track.artist, + album: track.album, + artBytes: artBytes, + ); + + // Update theme from album art + final seedColorNotifier = _container!.read( + seedColorProvider.notifier, + ); + seedColorNotifier.updateFromAlbumArtBytes(artBytes); } } else { - // For local tracks, use existing metadata service - // TODO: Get metadata service working + // For local tracks, use metadata service + final metadataService = MetadataService(); + metadata = await metadataService.getMetadata(mediaItem.id); + + // Update theme from album art + final seedColorNotifier = _container!.read(seedColorProvider.notifier); + seedColorNotifier.updateFromAlbumArtBytes(metadata.artBytes); } - // Reset to default for now - final seedColorNotifier = _container!.read(seedColorProvider.notifier); - seedColorNotifier.resetToDefault(); + // Set current track metadata + if (metadata != null) { + final metadataNotifier = _container!.read( + currentTrackMetadataProvider.notifier, + ); + metadataNotifier.setMetadata(metadata); + } } catch (e) { - // If metadata retrieval fails, reset to default color + // If metadata retrieval fails, reset to default color and clear metadata final seedColorNotifier = _container!.read(seedColorProvider.notifier); seedColorNotifier.resetToDefault(); + + final metadataNotifier = _container!.read( + currentTrackMetadataProvider.notifier, + ); + metadataNotifier.clear(); } } diff --git a/lib/logic/metadata_service.dart b/lib/logic/metadata_service.dart index 582f273..e652207 100644 --- a/lib/logic/metadata_service.dart +++ b/lib/logic/metadata_service.dart @@ -1,10 +1,9 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter_media_metadata/flutter_media_metadata.dart'; -import 'package:http/http.dart' as http; -import 'package:riverpod_annotation/riverpod_annotation.dart'; -import 'package:groovybox/providers/remote_provider.dart'; import 'package:groovybox/providers/db_provider.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:http/http.dart' as http; class TrackMetadata { final String? title; @@ -38,20 +37,16 @@ class MetadataService { } } -@Riverpod(keepAlive: true) -MetadataService metadataService(Ref ref) { - return MetadataService(); -} +final trackMetadataProvider = FutureProvider.family(( + ref, + path, +) async { + try { + // Import the database provider directly + final db = ref.watch(databaseProvider); -@riverpod -Future trackMetadata(Ref ref, String path) async { - // Check if this is a remote track (protocol URL) - final urlResolver = ref.watch(remoteUrlResolverProvider); - if (urlResolver.isProtocolUrl(path)) { - // For remote tracks, get metadata from database - final database = ref.watch(databaseProvider); - final track = await (database.select( - database.tracks, + final track = await (db.select( + db.tracks, )..where((t) => t.path.equals(path))).getSingleOrNull(); if (track != null) { @@ -75,11 +70,21 @@ Future trackMetadata(Ref ref, String path) async { album: track.album, artBytes: artBytes, ); + } else { + return TrackMetadata( + title: 'Unknown Title', + artist: 'Unknown Artist', + album: 'Unknown Album', + artBytes: null, + ); } - return TrackMetadata(); - } else { - // For local tracks, use file metadata - final service = MetadataService(); - return service.getMetadata(path); + } catch (e) { + debugPrint('Error fetching metadata for $path: $e'); + return TrackMetadata( + title: 'Unknown Title', + artist: 'Unknown Artist', + album: 'Unknown Album', + artBytes: null, + ); } -} +}); diff --git a/lib/providers/audio_provider.dart b/lib/providers/audio_provider.dart index ab017ee..bc80db2 100644 --- a/lib/providers/audio_provider.dart +++ b/lib/providers/audio_provider.dart @@ -1,4 +1,5 @@ import 'package:groovybox/logic/audio_handler.dart'; +import 'package:groovybox/logic/metadata_service.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'audio_provider.g.dart'; @@ -15,3 +16,19 @@ AudioHandler audioHandler(Ref ref) { void setAudioHandler(AudioHandler handler) { _audioHandler = handler; } + +@Riverpod(keepAlive: true) +class CurrentTrackMetadataNotifier extends _$CurrentTrackMetadataNotifier { + @override + TrackMetadata? build() { + return null; + } + + void setMetadata(TrackMetadata metadata) { + state = metadata; + } + + void clear() { + state = null; + } +} diff --git a/lib/providers/audio_provider.g.dart b/lib/providers/audio_provider.g.dart index 468558f..ee449e7 100644 --- a/lib/providers/audio_provider.g.dart +++ b/lib/providers/audio_provider.g.dart @@ -49,3 +49,58 @@ final class AudioHandlerProvider } String _$audioHandlerHash() => r'65fbd92e049fe4f3a0763516f1e68e1614f7630f'; + +@ProviderFor(CurrentTrackMetadataNotifier) +const currentTrackMetadataProvider = CurrentTrackMetadataNotifierProvider._(); + +final class CurrentTrackMetadataNotifierProvider + extends $NotifierProvider { + const CurrentTrackMetadataNotifierProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'currentTrackMetadataProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$currentTrackMetadataNotifierHash(); + + @$internal + @override + CurrentTrackMetadataNotifier create() => CurrentTrackMetadataNotifier(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(TrackMetadata? value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } +} + +String _$currentTrackMetadataNotifierHash() => + r'0a491bd4edda2b010ed3d6f7dd459f4ac8689a5f'; + +abstract class _$CurrentTrackMetadataNotifier + extends $Notifier { + TrackMetadata? build(); + @$mustCallSuper + @override + void runBuild() { + final created = build(); + final ref = this.ref as $Ref; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, + TrackMetadata?, + Object?, + Object? + >; + element.handleValue(ref, created); + } +} diff --git a/lib/ui/screens/player_screen.dart b/lib/ui/screens/player_screen.dart index 1140763..4ad15d7 100644 --- a/lib/ui/screens/player_screen.dart +++ b/lib/ui/screens/player_screen.dart @@ -48,46 +48,37 @@ class PlayerScreen extends HookConsumerWidget { } final media = medias[index]; - final path = Uri.decodeFull(Uri.parse(media.uri).path); - // For now, skip metadata loading to avoid provider issues - final AsyncValue metadataAsync = AsyncValue.data( - TrackMetadata(), - ); + final currentMetadata = ref.watch(currentTrackMetadataProvider); // Build blurred background if cover art is available Widget? background; - metadataAsync.when( - data: (meta) { - if (meta.artBytes != null) { - background = Positioned.fill( - child: Stack( - children: [ - Container( - decoration: BoxDecoration( - image: DecorationImage( - image: MemoryImage(meta.artBytes!), - fit: BoxFit.cover, - ), - ), + final artBytes = currentMetadata?.artBytes; + if (artBytes != null) { + background = Positioned.fill( + child: Stack( + children: [ + Container( + decoration: BoxDecoration( + image: DecorationImage( + image: MemoryImage(artBytes), + fit: BoxFit.cover, ), - BackdropFilter( - filter: ImageFilter.blur(sigmaX: 50, sigmaY: 50), - child: Container( - color: Theme.of( - context, - ).colorScheme.surface.withValues(alpha: 0.6), - ), - ), - ], + ), ), - ); - } else { - background = null; - } - }, - loading: () => background = null, - error: (_, _) => background = null, - ); + BackdropFilter( + filter: ImageFilter.blur(sigmaX: 50, sigmaY: 50), + child: Container( + color: Theme.of( + context, + ).colorScheme.surface.withValues(alpha: 0.6), + ), + ), + ], + ), + ); + } else { + background = null; + } final devicePadding = MediaQuery.paddingOf(context); @@ -116,7 +107,7 @@ class PlayerScreen extends HookConsumerWidget { body: ClipRect( child: Stack( children: [ - ...background != null ? [background!] : [], + ...background != null ? [background] : [], // Main content (StreamBuilder) Builder( builder: (context) { @@ -126,18 +117,16 @@ class PlayerScreen extends HookConsumerWidget { child: _MobileLayout( player: player, viewMode: viewMode, - metadataAsync: metadataAsync, media: media, - trackPath: path, + trackPath: media.uri, ), ); } else { return _DesktopLayout( player: player, viewMode: viewMode, - metadataAsync: metadataAsync, media: media, - trackPath: path, + trackPath: media.uri, ); } }, @@ -164,30 +153,30 @@ class PlayerScreen extends HookConsumerWidget { } } -class _MobileLayout extends StatelessWidget { +class _MobileLayout extends HookConsumerWidget { final Player player; final ValueNotifier viewMode; - final AsyncValue metadataAsync; final Media media; final String trackPath; const _MobileLayout({ required this.player, required this.viewMode, - required this.metadataAsync, required this.media, required this.trackPath, }); @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { + final currentMetadata = ref.watch(currentTrackMetadataProvider); + return AnimatedSwitcher( duration: const Duration(milliseconds: 300), child: switch (viewMode.value) { ViewMode.cover => _CoverView( key: const ValueKey('cover'), player: player, - metadataAsync: metadataAsync, + currentMetadata: currentMetadata, media: media, trackPath: trackPath, ).padding(bottom: MediaQuery.paddingOf(context).bottom), @@ -207,13 +196,13 @@ class _MobileLayout extends StatelessWidget { class _PlayerCoverControlsPanel extends StatelessWidget { final Player player; - final AsyncValue metadataAsync; + final TrackMetadata? currentMetadata; final Media media; final String trackPath; const _PlayerCoverControlsPanel({ required this.player, - required this.metadataAsync, + required this.currentMetadata, required this.media, required this.trackPath, }); @@ -235,7 +224,7 @@ class _PlayerCoverControlsPanel extends StatelessWidget { constraints: const BoxConstraints(maxWidth: 400), child: AspectRatio( aspectRatio: 1, - child: _PlayerCoverArt(metadataAsync: metadataAsync), + child: _PlayerCoverArt(currentMetadata: currentMetadata), ), ), ), @@ -243,7 +232,7 @@ class _PlayerCoverControlsPanel extends StatelessWidget { ), _PlayerControls( player: player, - metadataAsync: metadataAsync, + currentMetadata: currentMetadata, media: media, trackPath: trackPath, ), @@ -254,23 +243,23 @@ class _PlayerCoverControlsPanel extends StatelessWidget { } } -class _DesktopLayout extends StatelessWidget { +class _DesktopLayout extends HookConsumerWidget { final Player player; final ValueNotifier viewMode; - final AsyncValue metadataAsync; final Media media; final String trackPath; const _DesktopLayout({ required this.player, required this.viewMode, - required this.metadataAsync, required this.media, required this.trackPath, }); @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { + final currentMetadata = ref.watch(currentTrackMetadataProvider); + return AnimatedSwitcher( duration: const Duration(milliseconds: 300), child: switch (viewMode.value) { @@ -278,7 +267,7 @@ class _DesktopLayout extends StatelessWidget { key: const ValueKey('cover'), child: _PlayerCoverControlsPanel( player: player, - metadataAsync: metadataAsync, + currentMetadata: currentMetadata, media: media, trackPath: trackPath, ), @@ -294,7 +283,7 @@ class _DesktopLayout extends StatelessWidget { child: Center( child: _PlayerCoverControlsPanel( player: player, - metadataAsync: metadataAsync, + currentMetadata: currentMetadata, media: media, trackPath: trackPath, ), @@ -328,7 +317,7 @@ class _DesktopLayout extends StatelessWidget { child: Center( child: _PlayerCoverControlsPanel( player: player, - metadataAsync: metadataAsync, + currentMetadata: currentMetadata, media: media, trackPath: trackPath, ), @@ -355,14 +344,14 @@ class _DesktopLayout extends StatelessWidget { class _CoverView extends StatelessWidget { final Player player; - final AsyncValue metadataAsync; + final TrackMetadata? currentMetadata; final Media media; final String trackPath; const _CoverView({ super.key, required this.player, - required this.metadataAsync, + required this.currentMetadata, required this.media, required this.trackPath, }); @@ -377,13 +366,13 @@ class _CoverView extends StatelessWidget { child: Padding( padding: const EdgeInsets.only(bottom: 32), child: Center( - child: _PlayerCoverArt(metadataAsync: metadataAsync), + child: _PlayerCoverArt(currentMetadata: currentMetadata), ), ), ), _PlayerControls( player: player, - metadataAsync: metadataAsync, + currentMetadata: currentMetadata, media: media, trackPath: trackPath, ), @@ -417,64 +406,49 @@ class _LyricsView extends StatelessWidget { } class _PlayerCoverArt extends StatelessWidget { - final AsyncValue metadataAsync; + final TrackMetadata? currentMetadata; - const _PlayerCoverArt({required this.metadataAsync}); + const _PlayerCoverArt({required this.currentMetadata}); @override Widget build(BuildContext context) { - return metadataAsync.when( - data: (meta) => Center( - child: AspectRatio( - aspectRatio: 1, - child: Container( - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surfaceContainer, - borderRadius: BorderRadius.circular(24), - boxShadow: [ - BoxShadow( - color: Theme.of( - context, - ).colorScheme.shadow.withValues(alpha: 0.3), - blurRadius: 20, - offset: const Offset(0, 10), - ), - ], - image: meta.artBytes != null + return Center( + child: AspectRatio( + aspectRatio: 1, + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surfaceContainer, + borderRadius: BorderRadius.circular(24), + boxShadow: [ + BoxShadow( + color: Theme.of( + context, + ).colorScheme.shadow.withValues(alpha: 0.3), + blurRadius: 20, + offset: const Offset(0, 10), + ), + ], + image: () { + final artBytes = currentMetadata?.artBytes; + return artBytes != null ? DecorationImage( - image: MemoryImage(meta.artBytes!), + image: MemoryImage(artBytes), fit: BoxFit.cover, ) - : null, - ), - child: meta.artBytes == null - ? Center( - child: Icon( - Icons.music_note, - size: 80, - color: Theme.of( - context, - ).colorScheme.onSurface.withValues(alpha: 0.7), - ), - ) - : null, - ), - ), - ), - loading: () => const Center(child: CircularProgressIndicator()), - error: (_, _) => Container( - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surfaceContainer, - borderRadius: BorderRadius.circular(24), - ), - child: Center( - child: Icon( - Icons.error_outline, - size: 80, - color: Theme.of( - context, - ).colorScheme.onSurface.withValues(alpha: 0.7), + : null; + }(), ), + child: currentMetadata?.artBytes == null + ? Center( + child: Icon( + Icons.music_note, + size: 80, + color: Theme.of( + context, + ).colorScheme.onSurface.withValues(alpha: 0.7), + ), + ) + : null, ), ), ); @@ -1161,7 +1135,7 @@ class _QueueView extends HookConsumerWidget { itemBuilder: (context, index) { final media = playlist.medias[index]; final isCurrent = index == playlist.index; - final trackPath = Uri.decodeFull(Uri.parse(media.uri).path); + final trackPath = media.uri; final trackAsync = ref.watch(trackByPathProvider(trackPath)); return trackAsync.when( @@ -1525,13 +1499,13 @@ class _TimedLyricsView extends HookConsumerWidget { class _PlayerControls extends HookWidget { final Player player; - final AsyncValue metadataAsync; + final TrackMetadata? currentMetadata; final Media media; final String trackPath; const _PlayerControls({ required this.player, - required this.metadataAsync, + required this.currentMetadata, required this.media, required this.trackPath, }); @@ -1547,27 +1521,19 @@ class _PlayerControls extends HookWidget { // Title & Artist Column( children: [ - metadataAsync.when( - data: (meta) => Text( - meta.title ?? Uri.parse(media.uri).pathSegments.last, - style: Theme.of(context).textTheme.headlineSmall, - textAlign: TextAlign.center, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - loading: () => const SizedBox(height: 32), - error: (_, _) => Text(Uri.parse(media.uri).pathSegments.last), + Text( + currentMetadata?.title ?? Uri.parse(media.uri).pathSegments.last, + style: Theme.of(context).textTheme.headlineSmall, + textAlign: TextAlign.center, + maxLines: 1, + overflow: TextOverflow.ellipsis, ), const SizedBox(height: 8), - metadataAsync.when( - data: (meta) => Text( - meta.artist ?? 'Unknown Artist', - style: Theme.of(context).textTheme.bodyLarge?.copyWith( - color: Theme.of(context).colorScheme.primary, - ), + Text( + currentMetadata?.artist ?? 'Unknown Artist', + style: Theme.of(context).textTheme.bodyLarge?.copyWith( + color: Theme.of(context).colorScheme.primary, ), - loading: () => const SizedBox(height: 24), - error: (_, _) => const SizedBox.shrink(), ), ], ), diff --git a/lib/ui/widgets/mini_player.dart b/lib/ui/widgets/mini_player.dart index a8e9039..86b2c3d 100644 --- a/lib/ui/widgets/mini_player.dart +++ b/lib/ui/widgets/mini_player.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:groovybox/data/db.dart' as db; -import 'package:groovybox/logic/metadata_service.dart'; + import 'package:groovybox/providers/audio_provider.dart'; import 'package:groovybox/ui/screens/player_screen.dart'; import 'package:groovybox/ui/widgets/track_tile.dart'; @@ -53,10 +53,7 @@ class _MobileMiniPlayer extends HookConsumerWidget { final devicePadding = MediaQuery.paddingOf(context); - // For now, skip metadata loading to avoid provider issues - final AsyncValue metadataAsync = AsyncValue.data( - TrackMetadata(), - ); + final currentMetadata = ref.watch(currentTrackMetadataProvider); Widget content = Container( height: 72 + devicePadding.bottom, @@ -132,19 +129,18 @@ class _MobileMiniPlayer extends HookConsumerWidget { // Cover Art AspectRatio( aspectRatio: 1, - child: metadataAsync.when( - data: (meta) => meta.artBytes != null - ? Image.memory(meta.artBytes!, fit: BoxFit.cover) - : Container( - color: Colors.grey[800], - child: const Icon( - Icons.music_note, - color: Colors.white54, - ), + child: currentMetadata?.artBytes != null + ? Image.memory( + currentMetadata!.artBytes!, + fit: BoxFit.cover, + ) + : Container( + color: Colors.grey[800], + child: const Icon( + Icons.music_note, + color: Colors.white54, ), - loading: () => Container(color: Colors.grey[800]), - error: (_, _) => Container(color: Colors.grey[800]), - ), + ), ), const Gap(8), // Title & Artist @@ -155,28 +151,19 @@ class _MobileMiniPlayer extends HookConsumerWidget { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ - metadataAsync.when( - data: (meta) => Text( - meta.title ?? - Uri.parse(media.uri).pathSegments.last, - style: Theme.of(context).textTheme.bodyMedium - ?.copyWith(fontWeight: FontWeight.bold), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - loading: () => const Text('Loading...'), - error: (_, _) => - Text(Uri.parse(media.uri).pathSegments.last), + Text( + currentMetadata?.title ?? + Uri.parse(media.uri).pathSegments.last, + style: Theme.of(context).textTheme.bodyMedium + ?.copyWith(fontWeight: FontWeight.bold), + maxLines: 1, + overflow: TextOverflow.ellipsis, ), - metadataAsync.when( - data: (meta) => Text( - meta.artist ?? 'Unknown Artist', - style: Theme.of(context).textTheme.bodySmall, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - loading: () => const SizedBox.shrink(), - error: (_, _) => const SizedBox.shrink(), + Text( + currentMetadata?.artist ?? 'Unknown Artist', + style: Theme.of(context).textTheme.bodySmall, + maxLines: 1, + overflow: TextOverflow.ellipsis, ), ], ), @@ -272,10 +259,7 @@ class _DesktopMiniPlayer extends HookConsumerWidget { final devicePadding = MediaQuery.paddingOf(context); - // For now, skip metadata loading to avoid provider issues - final AsyncValue metadataAsync = AsyncValue.data( - TrackMetadata(), - ); + final currentMetadata = ref.watch(currentTrackMetadataProvider); Widget content = Container( height: 72 + devicePadding.bottom, @@ -356,23 +340,18 @@ class _DesktopMiniPlayer extends HookConsumerWidget { // Cover Art AspectRatio( aspectRatio: 1, - child: metadataAsync.when( - data: (meta) => meta.artBytes != null - ? Image.memory( - meta.artBytes!, - fit: BoxFit.cover, - ) - : Container( - color: Colors.grey[800], - child: const Icon( - Icons.music_note, - color: Colors.white54, - ), + child: currentMetadata?.artBytes != null + ? Image.memory( + currentMetadata!.artBytes!, + fit: BoxFit.cover, + ) + : Container( + color: Colors.grey[800], + child: const Icon( + Icons.music_note, + color: Colors.white54, ), - loading: () => Container(color: Colors.grey[800]), - error: (_, _) => - Container(color: Colors.grey[800]), - ), + ), ), const Gap(8), // Title & Artist @@ -384,33 +363,19 @@ class _DesktopMiniPlayer extends HookConsumerWidget { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ - metadataAsync.when( - data: (meta) => Text( - meta.title ?? - Uri.parse(media.uri).pathSegments.last, - style: Theme.of(context) - .textTheme - .bodyMedium - ?.copyWith(fontWeight: FontWeight.bold), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - loading: () => const Text('Loading...'), - error: (_, _) => Text( - Uri.parse(media.uri).pathSegments.last, - ), + Text( + currentMetadata?.title ?? + Uri.parse(media.uri).pathSegments.last, + style: Theme.of(context).textTheme.bodyMedium + ?.copyWith(fontWeight: FontWeight.bold), + maxLines: 1, + overflow: TextOverflow.ellipsis, ), - metadataAsync.when( - data: (meta) => Text( - meta.artist ?? 'Unknown Artist', - style: Theme.of( - context, - ).textTheme.bodySmall, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - loading: () => const SizedBox.shrink(), - error: (_, _) => const SizedBox.shrink(), + Text( + currentMetadata?.artist ?? 'Unknown Artist', + style: Theme.of(context).textTheme.bodySmall, + maxLines: 1, + overflow: TextOverflow.ellipsis, ), ], ), diff --git a/pubspec.lock b/pubspec.lock index 704efc7..e1d8bcd 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -537,7 +537,7 @@ packages: source: hosted version: "0.15.6" http: - dependency: transitive + dependency: "direct main" description: name: http sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" diff --git a/pubspec.yaml b/pubspec.yaml index a617343..558b8b3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -51,6 +51,7 @@ dependencies: styled_widget: ^0.4.1 super_sliver_list: ^0.4.1 dio: ^5.0.0 + http: ^1.2.2 audio_service: ^0.18.18 palette_generator: ^0.3.3+4 watcher: ^1.2.0