
Caches user input to manage race conditions effectively during value changes, enabling seamless integration with existing data management workflows in application development.
compose-cache is a lightweight utility for Jetpack Compose that helps you handle two-way state synchronization between UI and external sources (e.g. databases, flows) β without suffering from cursor jumps or overwritten user input.
[!NOTE]
π RevealSwipe is now Compose Multiplatform
In Compose, text fields are typically bound to a single source of truth β for example, a StateFlow or immutable UI state provided by a ViewModel.
When the user types, you usually:
onValueChange)combine, copy, or other transformations)TextField value accordinglyThe issue arises because state emission and recomposition are asynchronous. If the user types quickly, there can be a short delay before the ViewModel emits the updated state. During that window:
TextField still receives the old value from the last emissionThis can happen even without databases β simply using collectAsState() with a ViewModel is enough to reproduce it, especially in multi-field forms or with combined UI state.
rememberForUserInput wraps your state with a temporary local cache.
When the user types, it stores the input locally and marks the state as dirty.
While dirty, external updates are ignored to prevent overwriting user input.
Once the external source emits the same value that the user typed, the dirty flag is cleared and control is handed back to the source.
π This means the UI stays responsive, and your database stays the single source of truth β without flicker or cursor issues.
Add actual compose-cache library:
dependencies {
implementation 'de.charlex.compose:compose-cache:3.0.1'
}rememberForUserInput can be used for every value/onValueChange behaviors where race conditions occurs
val (text, onTextChange) = rememberForUserInput(
value = dbValue,
onValueChange = { newValue ->
viewModel.updateDatabase(newValue)
}
)
TextField(
value = text,
onValueChange = onTextChange
)If your value isnβt a simple primitive, you can provide a custom equals function:
val (item, onItemChange) = rememberForUserInput(
value = selectedItem,
equals = { a, b -> a.id == b.id },
onValueChange = { newItem -> updateInDb(newItem) }
)Copyright 2022 Alexander Karkossa
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
compose-cache is a lightweight utility for Jetpack Compose that helps you handle two-way state synchronization between UI and external sources (e.g. databases, flows) β without suffering from cursor jumps or overwritten user input.
[!NOTE]
π RevealSwipe is now Compose Multiplatform
In Compose, text fields are typically bound to a single source of truth β for example, a StateFlow or immutable UI state provided by a ViewModel.
When the user types, you usually:
onValueChange)combine, copy, or other transformations)TextField value accordinglyThe issue arises because state emission and recomposition are asynchronous. If the user types quickly, there can be a short delay before the ViewModel emits the updated state. During that window:
TextField still receives the old value from the last emissionThis can happen even without databases β simply using collectAsState() with a ViewModel is enough to reproduce it, especially in multi-field forms or with combined UI state.
rememberForUserInput wraps your state with a temporary local cache.
When the user types, it stores the input locally and marks the state as dirty.
While dirty, external updates are ignored to prevent overwriting user input.
Once the external source emits the same value that the user typed, the dirty flag is cleared and control is handed back to the source.
π This means the UI stays responsive, and your database stays the single source of truth β without flicker or cursor issues.
Add actual compose-cache library:
dependencies {
implementation 'de.charlex.compose:compose-cache:3.0.1'
}rememberForUserInput can be used for every value/onValueChange behaviors where race conditions occurs
val (text, onTextChange) = rememberForUserInput(
value = dbValue,
onValueChange = { newValue ->
viewModel.updateDatabase(newValue)
}
)
TextField(
value = text,
onValueChange = onTextChange
)If your value isnβt a simple primitive, you can provide a custom equals function:
val (item, onItemChange) = rememberForUserInput(
value = selectedItem,
equals = { a, b -> a.id == b.id },
onValueChange = { newItem -> updateInDb(newItem) }
)Copyright 2022 Alexander Karkossa
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.