Always force recompose if parent providers of Composition have changed
Forces full recomposition if composition local map provided by parent composition has been updated.
Fixes: 258089076
Test: SubcomposeLayoutTest
Change-Id: I86ec7162e9b7ff0c55e30ca85d79139ac7bd1190
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
index 03e25a0..65ee2ec 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
@@ -3314,7 +3314,7 @@
invokeComposable(this, content)
endGroup()
} else if (
- forciblyRecompose &&
+ (forciblyRecompose || providersInvalid) &&
savedContent != null &&
savedContent != Composer.Empty
) {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt
index a71891f..f3a63ae 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt
@@ -64,6 +64,7 @@
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
@@ -1198,6 +1199,57 @@
}
@Test
+ fun staticCompositionLocalChangeInMainComposition_withNonStaticLocal_invalidatesComposition() {
+ var isDark by mutableStateOf(false)
+
+ val staticLocal = staticCompositionLocalOf<Boolean> { error("Not defined") }
+ val local = compositionLocalOf<Boolean> { error("Not defined") }
+ val innerLocal = staticCompositionLocalOf<Unit> { error("\not defined") }
+
+ val content = @Composable {
+ CompositionLocalProvider(innerLocal provides Unit) {
+ val value1 = staticLocal.current
+ val value2 = local.current
+ Box(
+ Modifier
+ .testTag(if (value1) "dark" else "light")
+ .requiredSize(if (value2) 50.dp else 100.dp)
+ )
+ }
+ }
+
+ rule.setContent {
+ CompositionLocalProvider(
+ staticLocal provides isDark,
+ ) {
+ CompositionLocalProvider(
+ local provides staticLocal.current
+ ) {
+ SubcomposeLayout { constraints ->
+ val measurables = subcompose(Unit, content)
+ val placeables = measurables.map {
+ it.measure(constraints)
+ }
+ layout(100, 100) {
+ placeables.forEach { it.place(IntOffset.Zero) }
+ }
+ }
+ }
+ }
+ }
+
+ rule.onNodeWithTag("light")
+ .assertWidthIsEqualTo(100.dp)
+
+ isDark = true
+
+ rule.waitForIdle()
+
+ rule.onNodeWithTag("dark")
+ .assertWidthIsEqualTo(50.dp)
+ }
+
+ @Test
fun derivedStateChangeInMainCompositionRecomposesSubcomposition() {
var flag by mutableStateOf(true)
var subcomposionValue: Boolean? = null