Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Material 3 Slider goes out of draggable state prematurely in some cases #4366

Closed
badmannersteam opened this issue Feb 25, 2024 · 3 comments · Fixed by JetBrains/compose-multiplatform-core#1135
Labels
bug Something isn't working desktop regression

Comments

@badmannersteam
Copy link
Contributor

Describe the bug
In some cases (see example below) I have the following slider behavior:

  • I press on slider track and start dragging
  • slider calls onValueChange 1-3 times
  • slider calls onValueChangeFinished (when mouse button is still pressed)
  • slider becomes unresponsible until I release a mouse button

Affected platforms

  • Desktop

Versions

  • Kotlin version: 1.9.22
  • Compose Multiplatform version: 1.6.0-rc03
  • OS version: Windows 11 Pro 23H2 22631.3155
  • OS architecture: x86
  • JDK: corretto-17.0.9

To Reproduce

import androidx.compose.material3.Slider
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import kotlinx.coroutines.flow.MutableStateFlow

class Store(
    val value: MutableStateFlow<Float> = MutableStateFlow(0.5f)
) {
    fun progress(it: Float) {
        value.tryEmit(it)
        println(it)
    }
    fun finished() = println("finished")
}

fun main() {
    val store = Store()
    application {
        Window(::exitApplication) {
            val value by store.value.collectAsState()
            Slider(
                value = value,
                onValueChange = { store.progress(it) },
                onValueChangeFinished = { store.finished() }
            )
        }
    }
}

Run this and try to drag the slider.

Expected behavior
Slider should moves and doesn't go out of draggable state before I release the mouse button.

Additional context
Issue doesn't reproduce in Compose 1.5.12.
Looks like the issue was introduced in this commit - JetBrains/compose-multiplatform-core@f88a09b#diff-407454b5953648c917a33db1d037c1f5f8279ec22e572db656dbd9479f6b129a, maybe because before this commit gestureEndAction was remembered in rememberUpdatedState {}.

I don't know exactly what is the problem, but if I inline the class all works fine:

val value: MutableStateFlow<Float> = MutableStateFlow(0.5f)
fun progress(it: Float) {
    value.tryEmit(it)
    println(it)
}
fun finished() = println("finished")

fun main() {
    application {
        Window(::exitApplication) {
            val value by value.collectAsState()
            Slider(
                value = value,
                onValueChange = { progress(it) },
                onValueChangeFinished = { finished() }
            )
        }
    }
}

Also remembering of onValueChangeFinished value resolves the problem as a workaround:

fun main() {
    val store = Store()
    application {
        Window(::exitApplication) {
            val value by store.value.collectAsState()
            val finished by remember { mutableStateOf(store::finished) }
            Slider(
                value = value,
                onValueChange = { store.progress(it) },
                onValueChangeFinished = finished
            )
        }
    }
}
@badmannersteam badmannersteam added bug Something isn't working submitted labels Feb 25, 2024
@dima-avdeev-jb
Copy link
Contributor

Thanks! Yes this bug appears also on MacOS in Compose for Desktop 1.6.0-rc03.
With version 1.5.12 Slider works well.

@igordmn
Copy link
Collaborator

igordmn commented Feb 26, 2024

It is reproducible on Android as well (Jetpack Compose 1.6, Compiler 1.5.8):

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.Slider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import kotlinx.coroutines.flow.MutableStateFlow

val store = Store()

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            Screen(store)
        }
    }
}

class Store(
    val value: MutableStateFlow<Float> = MutableStateFlow(0.5f)
) {
    fun progress(it: Float) {
        value.tryEmit(it)
        println(it)
    }
    fun finished() = println("finished")
}

@Composable
fun Screen(store: Store) {
    val value by store.value.collectAsState()
    Slider(
        value = value,
        onValueChange = { store.progress(it) },
        onValueChangeFinished = { store.finished() }
    )
}

@igordmn
Copy link
Collaborator

igordmn commented Feb 26, 2024

A temporary workaround until we make a stable release - remember store:

    val currentStore by rememberUpdatedState(store)
    val value by store.value.collectAsState()
    Slider(
        value = value,
        onValueChange = { currentStore.progress(it) },
        onValueChangeFinished = { currentStore.finished() }
    )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working desktop regression
Projects
None yet
3 participants