JetBrains / compose-multiplatform

Compose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.
https://jetbrains.com/lp/compose-multiplatform
Apache License 2.0
16.26k stars 1.18k forks source link

java.lang.IllegalStateException when toggling switches in nested scrollViews #3204

Closed Lefix2 closed 1 year ago

Lefix2 commented 1 year ago

Describe the bug When using switches (and perhaps other toggle components) in nested scrollViews (one vertical and one horizontal), if we interact with a conditionnal composable, the app crash in some conditions.

Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: LayoutCoordinates androidx.compose.ui.node.ModifiedLayoutNode@28f86b4b is not attached!
    at androidx.compose.ui.node.LayoutNodeWrapper.localBoundingBoxOf(LayoutNodeWrapper.kt:711)
    at androidx.compose.foundation.gestures.ContentInViewModifier.onSizeChanged-O0kMr_c(Scrollable.kt:569)
    at androidx.compose.foundation.gestures.ContentInViewModifier.onRemeasured-ozmzZPI(Scrollable.kt:537)
    at androidx.compose.ui.node.LayoutNodeWrapper.onMeasured(LayoutNodeWrapper.kt:244)
    at androidx.compose.ui.node.ModifiedLayoutNode.measure-BRTryo0(ModifiedLayoutNode.kt:57)
    at androidx.compose.ui.graphics.SimpleGraphicsLayerModifier.measure-3p2s80s(GraphicsLayerModifier.kt:405)
    at androidx.compose.ui.node.ModifiedLayoutNode.measure-BRTryo0(ModifiedLayoutNode.kt:53)
    at androidx.compose.ui.node.LayoutNode$performMeasure$1.invoke(LayoutNode.kt:1428)
    at androidx.compose.ui.node.LayoutNode$performMeasure$1.invoke(LayoutNode.kt:1427)
    at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:2117)
    at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:113)
    at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui(OwnerSnapshotObserver.kt:78)
    at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui(OwnerSnapshotObserver.kt:66)
    at androidx.compose.ui.node.LayoutNode.performMeasure-BRTryo0$ui(LayoutNode.kt:1427)
    at androidx.compose.ui.node.OuterMeasurablePlaceable.remeasure-BRTryo0(OuterMeasurablePlaceable.kt:94)
    at androidx.compose.ui.node.LayoutNode.remeasure-_Sx5XlM$ui(LayoutNode.kt:1381)
    at androidx.compose.ui.node.LayoutNode.remeasure-_Sx5XlM$ui$default(LayoutNode.kt:1372)
    at androidx.compose.ui.node.MeasureAndLayoutDelegate.doRemeasure-sdFAvZA(MeasureAndLayoutDelegate.kt:187)
    at androidx.compose.ui.node.MeasureAndLayoutDelegate.remeasureAndRelayoutIfNeeded(MeasureAndLayoutDelegate.kt:274)
    at androidx.compose.ui.node.MeasureAndLayoutDelegate.access$remeasureAndRelayoutIfNeeded(MeasureAndLayoutDelegate.kt:38)
    at androidx.compose.ui.node.MeasureAndLayoutDelegate.measureAndLayout(MeasureAndLayoutDelegate.kt:208)
    at androidx.compose.ui.platform.SkiaBasedOwner.measureAndLayout(SkiaBasedOwner.skiko.kt:266)
    at androidx.compose.ui.node.Owner.measureAndLayout$default(Owner.kt:196)
    at androidx.compose.ui.ComposeScene.render(ComposeScene.skiko.kt:416)
    at androidx.compose.ui.awt.ComposeLayer$1$onRender$1.invoke(ComposeLayer.desktop.kt:326)
    at androidx.compose.ui.awt.ComposeLayer$1$onRender$1.invoke(ComposeLayer.desktop.kt:325)
    at androidx.compose.ui.awt.ComposeLayer.catchExceptions(ComposeLayer.desktop.kt:109)
    at androidx.compose.ui.awt.ComposeLayer.access$catchExceptions(ComposeLayer.desktop.kt:87)
    at androidx.compose.ui.awt.ComposeLayer$1.onRender(ComposeLayer.desktop.kt:325)
    at org.jetbrains.skiko.SkiaLayer.update$skiko(SkiaLayer.awt.kt:510)
    at org.jetbrains.skiko.redrawer.AWTRedrawer.update(AWTRedrawer.kt:54)
    at org.jetbrains.skiko.redrawer.LinuxOpenGLRedrawer$Companion$frameDispatcher$1.invokeSuspend(LinuxOpenGLRedrawer.kt:113)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
    at java.desktop/java.awt.event.InvocationEvent.dispatch(Unknown Source)
    at java.desktop/java.awt.EventQueue.dispatchEventImpl(Unknown Source)
    at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
    at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
    at java.base/java.security.AccessController.doPrivileged(Unknown Source)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
    at java.desktop/java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
    at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.desktop/java.awt.EventDispatchThread.run(Unknown Source)
    Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@7edd275e, SwingDispatcher@5e1bbf38]

Affected platforms

Versions

To Reproduce

var s1 by remember { mutableStateOf(true) }
var s2 by remember { mutableStateOf(true) }
var s3 by remember { mutableStateOf(true) }
Column( modifier = Modifier.verticalScroll(rememberScrollState())) {
    Switch( s1, { s1 = it })
    Switch( s2, { s2 = it })
    if (s2) {
        Row(modifier = Modifier.horizontalScroll(rememberScrollState())) {            
            Switch(s3, { s3 = it })
        }
    }
}

Expected behavior

dima-avdeev-jb commented 1 year ago

I tried the same code with Compose 1.4.0 and Kotlin 1.8.20, and it worked fine. Can you please try a newer version of Compose?

Lefix2 commented 1 year ago

I confirm, with recent version bug is not present! Thanks for the feedback

okushnikov commented 2 months ago

Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.