fix(player): seed full-player display state from current mediaItem on mount
Regression from v2026.05.13.2's load-then-swap rewrite. _displayedMedia only got populated by the ref.listen callback on mediaItem changes, but ref.listen doesn't fire on initial subscription — it only fires on transitions after the listener is registered. So opening the full player while a track was already playing left _displayedMedia null and the screen rendered "Nothing playing." even though the mini bar showed a live track. initState now reads the current mediaItem synchronously and seeds _displayedMedia immediately (and _displayedDominant from the color provider's cached value when available). A post-frame _scheduleSwap(current) runs to ensure the cover bytes are decoded and dominant color resolved when the user opens the player to a track whose album hasn't yet been color-extracted in this session.
This commit is contained in:
@@ -62,6 +62,39 @@ class _NowPlayingScreenState extends ConsumerState<NowPlayingScreen> {
|
||||
/// 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 —
|
||||
|
||||
Reference in New Issue
Block a user