fix(android): hold previous NowPlaying gradient color across track change
rememberDominantColor was keyed on coverUrl, so each track change reset the extracted color to Transparent and the gradient dipped toward the fallback before tweening to the new color. Drop the key so the held color stays on the previous track's dominant until the new palette resolves -- the gradient now tweens directly to the new color. Combined with CoverPrefetcher warming the next cover, this matches Flutter's preload-then-atomic-swap UX (no flash on track change) via native Compose mechanisms rather than a literal preload pipeline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -27,15 +27,21 @@ private const val GRADIENT_TWEEN_MS = 600
|
||||
* androidx.palette on a background thread, and animates the result
|
||||
* via [animateColorAsState] so track changes tween smoothly.
|
||||
*
|
||||
* Returns [Color.Transparent] while the bitmap is loading or on
|
||||
* any failure — callers should layer a fallback (e.g.
|
||||
* `MaterialTheme.colorScheme.surface`) underneath the gradient
|
||||
* tint so the screen never sits on a flat black.
|
||||
* The held color is NOT reset when [coverUrl] changes — it stays on
|
||||
* the previous track's dominant until the new palette resolves, so the
|
||||
* gradient tweens old→new directly instead of dipping toward the
|
||||
* fallback mid-swap. Mirrors `now_playing_screen.dart`'s preload-then-
|
||||
* swap ("keep the previous dominant"); the cover image swaps smoothly
|
||||
* via `CoverPrefetcher`, which warms the next track's bytes into Coil.
|
||||
*
|
||||
* Starts at [Color.Transparent] (cold mount) and resets to it only
|
||||
* when a track has no cover — callers layer a base color underneath so
|
||||
* the screen never sits on flat black.
|
||||
*/
|
||||
@Composable
|
||||
fun rememberDominantColor(coverUrl: String?): Color {
|
||||
val context = LocalContext.current
|
||||
var extracted by remember(coverUrl) { mutableStateOf(Color.Transparent) }
|
||||
var extracted by remember { mutableStateOf(Color.Transparent) }
|
||||
|
||||
LaunchedEffect(coverUrl) {
|
||||
if (coverUrl.isNullOrEmpty()) {
|
||||
@@ -47,6 +53,7 @@ fun rememberDominantColor(coverUrl: String?): Color {
|
||||
.allowHardware(false)
|
||||
.build()
|
||||
val result = SingletonImageLoader.get(context).execute(request)
|
||||
// Keep the previous dominant on load failure rather than dropping.
|
||||
if (result !is SuccessResult) return@LaunchedEffect
|
||||
val bitmap = result.image.toBitmap()
|
||||
val palette = withContext(Dispatchers.Default) {
|
||||
|
||||
Reference in New Issue
Block a user