fix(player): keep seek bar thumb at target on release

Previously, the Slider's value was switched from the local sliderPosition to
the positionMs prop the instant onValueChangeFinished cleared isScrubbing.
Because the prop had not yet caught up with the committed seek, the thumb
visibly snapped back to the pre-seek position before jumping to the target.

Drive the slider from sliderPosition unconditionally, sync sliderPosition
from the prop via a LaunchedEffect only when not scrubbing, and pin
sliderPosition to the committed target before clearing isScrubbing in
onValueChangeFinished.
This commit is contained in:
2026-06-21 11:17:16 +00:00
parent 67a141e7d7
commit b23db4bab3

View File

@@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Slider import androidx.compose.material3.Slider
import androidx.compose.material3.SliderDefaults import androidx.compose.material3.SliderDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@@ -33,7 +34,16 @@ fun PlayerSeekBar(
val currentPosition = positionMs.coerceIn(0, safeDuration) val currentPosition = positionMs.coerceIn(0, safeDuration)
var sliderPosition by remember { mutableFloatStateOf(currentPosition.toFloat()) } var sliderPosition by remember { mutableFloatStateOf(currentPosition.toFloat()) }
var isScrubbing by remember { mutableStateOf(false) } var isScrubbing by remember { mutableStateOf(false) }
val sliderValue = if (isScrubbing) sliderPosition else currentPosition.toFloat() val sliderValue = sliderPosition
// Keep sliderPosition in sync with the player's position when the user is not
// actively scrubbing. While scrubbing, the local drag value is preserved so the
// thumb does not snap back to a stale positionMs prop before the seek is committed.
LaunchedEffect(positionMs) {
if (!isScrubbing) {
sliderPosition = positionMs.toFloat()
}
}
Box( Box(
modifier = modifier modifier = modifier
@@ -64,6 +74,7 @@ fun PlayerSeekBar(
}, },
onValueChangeFinished = { onValueChangeFinished = {
val targetPosition = sliderPosition.toLong().coerceIn(0L, safeDuration) val targetPosition = sliderPosition.toLong().coerceIn(0L, safeDuration)
sliderPosition = targetPosition.toFloat()
isScrubbing = false isScrubbing = false
onSeek(targetPosition) onSeek(targetPosition)
}, },