diff --git a/flutter_client/lib/player/now_playing_screen.dart b/flutter_client/lib/player/now_playing_screen.dart index 4396cff3..818d3ea7 100644 --- a/flutter_client/lib/player/now_playing_screen.dart +++ b/flutter_client/lib/player/now_playing_screen.dart @@ -62,6 +62,39 @@ class _NowPlayingScreenState extends ConsumerState { /// a track whose cover hadn't finished decoding yet. String? _pendingPreloadId; + @override + void initState() { + super.initState(); + // Seed displayed state from the current mediaItem if a track is + // already playing when the full player is opened. ref.listen + // below only fires on CHANGES after subscription, so without + // this seed the screen sits on "Nothing playing." even though + // the mini bar shows a live track — the listener never gets a + // mediaItem emission because the stream's current value hasn't + // changed. + final current = ref.read(mediaItemProvider).value; + if (current == null) return; + _displayedMedia = current; + // Best-effort synchronous color seed: if albumColorProvider has + // already extracted this album's dominant color earlier in the + // session, pull it now so the backdrop renders correctly on + // first frame. Otherwise the post-frame _scheduleSwap below + // resolves it asynchronously and AnimatedContainer tweens. + final albumId = current.extras?['album_id'] as String?; + if (albumId != null && albumId.isNotEmpty) { + _displayedDominant = + ref.read(albumColorProvider(albumId)).asData?.value; + } + // Kick the preload pipeline post-frame so the cover bytes are + // decoded + the dominant color is resolved even when the user + // opens the player to a track that hasn't yet flowed through + // ref.listen. + WidgetsBinding.instance.addPostFrameCallback((_) { + if (!mounted) return; + _scheduleSwap(current); + }); + } + /// Preload the new track's cover bytes + dominant color, then /// atomically flip _displayedMedia / _displayedDominant. Until both /// resolve, the screen continues to render the previous track —