{"id":36420001,"url":"https://github.com/ch4rl3x/compose-cache","last_synced_at":"2026-01-11T17:05:40.066Z","repository":{"id":37976801,"uuid":"464455467","full_name":"ch4rl3x/compose-cache","owner":"ch4rl3x","description":"A lightweight Jetpack Compose utility that buffers user input locally until the backing state catches up — preventing cursor jumps and lost text when syncing with databases or flows.","archived":false,"fork":false,"pushed_at":"2025-10-30T09:04:03.000Z","size":1767,"stargazers_count":4,"open_issues_count":5,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-31T02:17:12.227Z","etag":null,"topics":["cache","compose","compose-multiplatform","kotlin-android","kotlin-ios","kotlin-multiplatform","remember","userinput"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ch4rl3x.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-02-28T11:29:54.000Z","updated_at":"2025-10-22T16:20:21.000Z","dependencies_parsed_at":"2023-10-14T16:54:49.665Z","dependency_job_id":"cefe0ebb-d3f4-4f90-908f-451e26db71b5","html_url":"https://github.com/ch4rl3x/compose-cache","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/ch4rl3x/compose-cache","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ch4rl3x%2Fcompose-cache","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ch4rl3x%2Fcompose-cache/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ch4rl3x%2Fcompose-cache/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ch4rl3x%2Fcompose-cache/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ch4rl3x","download_url":"https://codeload.github.com/ch4rl3x/compose-cache/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ch4rl3x%2Fcompose-cache/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28314264,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-11T14:58:17.114Z","status":"ssl_error","status_checked_at":"2026-01-11T14:55:53.580Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cache","compose","compose-multiplatform","kotlin-android","kotlin-ios","kotlin-multiplatform","remember","userinput"],"created_at":"2026-01-11T17:05:39.449Z","updated_at":"2026-01-11T17:05:40.060Z","avatar_url":"https://github.com/ch4rl3x.png","language":"Kotlin","readme":"\u003ca href=\"https://github.com/Ch4rl3x/compose-cache/actions?query=workflow%3ABuild\"\u003e\u003cimg src=\"https://github.com/ch4rl3x/compose-cache/actions/workflows/build.yml/badge.svg\" alt=\"Build\"\u003e\u003c/a\u003e\n\u003ca href=\"https://www.codefactor.io/repository/github/ch4rl3x/compose-cache\"\u003e\u003cimg src=\"https://www.codefactor.io/repository/github/ch4rl3x/compose-cache/badge\" alt=\"CodeFactor\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://repo1.maven.org/maven2/de/charlex/compose/compose-cache/\"\u003e\u003cimg src=\"https://img.shields.io/maven-central/v/de.charlex.compose/compose-cache\" alt=\"Maven Central\" /\u003e\u003c/a\u003e\n\n# compose-cache\n\n`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.\n\n\u003e [!NOTE]  \n\u003e 🚀 RevealSwipe is now Compose Multiplatform\n\n## 🧠 The Problem\n\nIn Compose, text fields are typically bound to a single source of truth — for example, a `StateFlow` or immutable UI state provided by a ViewModel.\nWhen the user types, you usually:\n\n1. Send the new input to the ViewModel (`onValueChange`)\n2. The ViewModel emits a new UI state (often via `combine`, `copy`, or other transformations)\n3. The UI collects this state and updates the `TextField` value accordingly\n\nThe issue arises because state emission and recomposition are asynchronous.\nIf the user types quickly, there can be a short delay before the ViewModel emits the updated state. During that window:\n\n* The `TextField` still receives the old value from the last emission\n* Compose updates the UI with that outdated value\n* This causes the cursor to jump or recently typed characters to disappear\n\nThis 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.\n\n## 🛠 The Solution\n\n`rememberForUserInput` wraps your state with a temporary local cache.\nWhen the user types, it stores the input locally and marks the state as dirty.\nWhile dirty, external updates are ignored to prevent overwriting user input.\nOnce the external source emits the same value that the user typed, the dirty flag is cleared and control is handed back to the source.\n\n👉 This means the UI stays responsive, and your database stays the single source of truth — without flicker or cursor issues.\n\n\n## Dependency\n\nAdd actual compose-cache library:\n\n```groovy\ndependencies {\n    implementation 'de.charlex.compose:compose-cache:3.0.1'\n}\n```\n\n## How does it work?\n\n`rememberForUserInput` can be used for every value/onValueChange behaviors where race conditions occurs\n\n```kotlin\nval (text, onTextChange) = rememberForUserInput(\n    value = dbValue,\n    onValueChange = { newValue -\u003e\n        viewModel.updateDatabase(newValue)\n    }\n)\n\nTextField(\n    value = text,\n    onValueChange = onTextChange\n)\n```\n\n### Optional: Custom equality check\n\nIf your value isn’t a simple primitive, you can provide a custom equals function:\n\n```kotlin\nval (item, onItemChange) = rememberForUserInput(\n    value = selectedItem,\n    equals = { a, b -\u003e a.id == b.id },\n    onValueChange = { newItem -\u003e updateInDb(newItem) }\n)\n```\n\nLicense\n--------\n\n    Copyright 2022 Alexander Karkossa\n    \n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n    \n       http://www.apache.org/licenses/LICENSE-2.0\n    \n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fch4rl3x%2Fcompose-cache","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fch4rl3x%2Fcompose-cache","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fch4rl3x%2Fcompose-cache/lists"}