From 1b126d5147ac2497fda182732d265e4f5308ef82 Mon Sep 17 00:00:00 2001 From: freetlab Date: Sat, 1 Aug 2026 19:19:30 +0200 Subject: [PATCH] fix(audio): publish idle from stop() instead of trusting the player just_audio's playerStateStream is .distinct() over a value-equal PlayerState, so stopping an already-idle player emits nothing. Paired with the source-change mask -- which writes loading into playbackState rather than filtering at read time -- a stop landing before native init completed would leave the state at loading forever. audio_service only tears the foreground service down on a non-idle to idle transition, so that window produced an unkillable notification stuck on "cargando" with a dead Stop button: strictly worse than the teardown this branch removes. Additive and idempotent -- when the player does emit its own idle, this just lands first. --- lib/servicios/servicio_audio.dart | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/servicios/servicio_audio.dart b/lib/servicios/servicio_audio.dart index 8265da8..1c11424 100644 --- a/lib/servicios/servicio_audio.dart +++ b/lib/servicios/servicio_audio.dart @@ -1253,6 +1253,26 @@ class PluriWaveAudioHandler extends BaseAudioHandler _cambiandoFuente = false; _revisionFuente++; await _player.stop(); + // Publish `idle` OURSELVES rather than trusting the player to emit it. + // `just_audio`'s `playerStateStream` is `.distinct()` over a value-equal + // `PlayerState`, so a stop landing on an already-idle player (a station + // change stopped before its native init finished pushing `loading`) + // emits NOTHING. Combined with the source-change mask above — which + // WRITES `loading` into `playbackState` rather than filtering at read + // time — that would leave the state stuck at `loading` forever: + // `audio_service` only tears the service down on a non-idle -> idle + // transition (`audio_service.dart:1131-1136`), so the notification would + // survive as an unkillable "cargando" with a dead Stop button. Strictly + // worse than the bug this branch fixes. Additive and idempotent: when + // the player DOES emit its own `idle`, this simply lands first and the + // duplicate is a no-op transition. + playbackState.add( + playbackState.value.copyWith( + processingState: AudioProcessingState.idle, + playing: false, + errorMessage: null, + ), + ); emisoraActual = null; mediaItem.add(null); await super.stop();