From 79f229de6d1957a5e2608fe8538dfdc4d71124b0 Mon Sep 17 00:00:00 2001 From: Jonas Haugesen Date: Thu, 10 Sep 2026 20:41:01 +0200 Subject: [PATCH] SearchBar: adopt outside text changes in a side effect SearchBar keeps a TextFieldValue so that the cursor ends up behind text that was changed from the outside (a search started by typing on a physical keyboard, or a reset search). That value was updated during composition, which is an anti-pattern: the value read earlier in the same composition is what gets drawn, so the write only schedules another recomposition pass of the search bar - and it can be dropped entirely if the composition does not complete. Do the same update in a SideEffect, after the composition. No behaviour change for the user. --- .../java/de/mm20/launcher2/ui/component/SearchBar.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/component/SearchBar.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/component/SearchBar.kt index 5480adcf4..a2b78c54b 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/component/SearchBar.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/component/SearchBar.kt @@ -24,6 +24,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.SideEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -143,8 +144,14 @@ fun SearchBar( val textFieldValue = remember { mutableStateOf(TextFieldValue(value, TextRange(value.length))) } - if (textFieldValue.value.text != value) { - textFieldValue.value = TextFieldValue(value, TextRange(value.length)) + // Characters the user typed are already in this value; onValueChange below keeps the cursor + // where the user put it. Text that changed from the outside - the search was reset, or a + // character was inserted by the physical keyboard handler - is adopted here, with the cursor at + // the end. This must not write state during composition, so it runs in a side effect after it. + SideEffect { + if (textFieldValue.value.text != value) { + textFieldValue.value = TextFieldValue(value, TextRange(value.length)) + } } LauncherCard(