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

Cannot disable new ExposedDropdownMenuBox (it is not recomposed) #3686

Closed
mahozad opened this issue Sep 15, 2023 · 2 comments · Fixed by JetBrains/compose-multiplatform-core#823
Assignees
Labels
bug Something isn't working reproduced

Comments

@mahozad
Copy link
Contributor

mahozad commented Sep 15, 2023

Describe the bug
I'm creating a custom exposed dropdown based on the new common one introduced in Compose Multiplatform 1.5.10-beta01.

It takes a parameter called isEnabled but cannot make the dropdown ignore clicks when the isEnabled is false.
See https://stackoverflow.com/a/74534475/8583692.

exposed-not-disabled.mp4

Affected platforms
Select one of the platforms below:

  • Desktop

Versions

  • Kotlin version*: 1.9.20-Beta
  • Compose Multiplatform version*: 1.5.10-beta01
  • OS version(s)* (required for Desktop and iOS issues): Window 11
  • OS architecture (x86 or arm64): x86_64
  • JDK (for desktop issues): Oracle JDK 17.0.5

To Reproduce

import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowDownward
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application

fun main() = application {
    Window(onCloseRequest = ::exitApplication) {
        var isEnabled by remember { mutableStateOf(true) }
        Row(verticalAlignment = Alignment.CenterVertically) {
            Button(onClick = { isEnabled = !isEnabled }) {
                Text(if (isEnabled) "Enabled" else "Disabled")
            }
            CustomExposedDropDown(
                label = { Text("Label") },
                items = (1..8).map { { Text("Option $it") } },
                isEnabled = isEnabled,
                onChange = {}
            )
        }
    }
}

@OptIn(ExperimentalMaterialApi::class)
@Composable
fun CustomExposedDropDown(
    label: @Composable BoxScope.() -> Unit,
    items: List<@Composable RowScope.() -> Unit>,
    isEnabled: Boolean,
    onChange: (Int) -> Unit,
) {
    var isExpanded by remember { mutableStateOf(false) }
    val iconRotation by animateFloatAsState(if (isExpanded) 180f else 0f)

    ExposedDropdownMenuBox(
        expanded = isExpanded,
        modifier = Modifier.width(200.dp),
        onExpandedChange = {
            // FIXME: The captured parameter isEnabled is always true here (why?) unless a recomposition occurs
            println("isEnabled = $isEnabled")
            if (isEnabled) isExpanded = it
        }
    ) {
        Box(
            contentAlignment = Alignment.Center,
            modifier = Modifier
                .fillMaxWidth()
                .heightIn(min = 36.dp)
                .background(color = MaterialTheme.colors.onSurface.copy(alpha = TextFieldDefaults.BackgroundOpacity))
                .clickable(enabled = isEnabled, onClick = {})
        ) {
            label()
            Icon(
                imageVector = Icons.Filled.ArrowDownward,
                contentDescription = "Messages.ICO_DSC_OPEN_DROPDOWN",
                tint = if (isEnabled) {
                    MaterialTheme.colors.primary
                } else {
                    MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled)
                },
                modifier = Modifier
                    .align(Alignment.CenterEnd)
                    .padding(end = 12.dp)
                    .size(12.dp)
                    .rotate(iconRotation)
            )
        }
        ExposedDropdownMenu(
            expanded = isExpanded,
            onDismissRequest = { isExpanded = false },
        ) {
            items.forEachIndexed { i, item ->
                DropdownMenuItem(
                    content = item,
                    onClick = {
                        onChange(i)
                        isExpanded = false
                    }
                )
            }
        }
    }
}
@mahozad mahozad added bug Something isn't working submitted labels Sep 15, 2023
@MatkovIvan MatkovIvan self-assigned this Sep 15, 2023
@MatkovIvan
Copy link
Member

Hi @mahozad, thanks for reporting it

// FIXME: isEnabled is always true here (why?) unless a recomposition occurs

That's the main problem here - onExpandedChange is cached internally.
Workaround for now: you can read current value using State<Boolean> (isEnabled.value) instead of just Boolean.

Affected platforms

  • Desktop

The current behaviour is the same on Android (Google's Jetpack Compose), so issue came from there. Therefore it requires fixing not only in our fork.

@mahozad
Copy link
Contributor Author

mahozad commented Sep 18, 2023

Thanks.

MatkovIvan added a commit to JetBrains/compose-multiplatform-core that referenced this issue Sep 25, 2023
## Proposed Changes

- Update callback between recompositions in `ExposedDropdownMenuBox`

This changes must be upstreamed

## Testing

Test: run code snippet from
JetBrains/compose-multiplatform#3686

## Issues Fixed

Fixes JetBrains/compose-multiplatform#3686
igordmn pushed a commit to JetBrains/compose-multiplatform-core that referenced this issue Nov 15, 2023
## Proposed Changes

- Update callback between recompositions in `ExposedDropdownMenuBox`

This changes must be upstreamed

## Testing

Test: run code snippet from
JetBrains/compose-multiplatform#3686

## Issues Fixed

Fixes JetBrains/compose-multiplatform#3686
igordmn pushed a commit to JetBrains/compose-multiplatform-core that referenced this issue Jan 30, 2024
## Proposed Changes

- Update callback between recompositions in `ExposedDropdownMenuBox`

This changes must be upstreamed

## Testing

Test: run code snippet from
JetBrains/compose-multiplatform#3686

## Issues Fixed

Fixes JetBrains/compose-multiplatform#3686
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working reproduced
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants