cashapp / cashapp/molecule

Scheduler breaks keyboard behavior for Compose UI consumers

Open
#63 17 comments 1 reaction 0 assignees View on GitHub
Dominant language
Kotlin
Stars
2.2k
Forks
116
Avg merge
1h 36m
Merged PRs (30d)
8

Description

Compose UI's `CoreTextField` is very picky about getting immediate updates to its values. The common (but useless) example seen in the docs, where the text state is held in a `by remember { mutableStateOf("") }`, has no problems updating the text field when typing on the keyboard quickly. However, if you provide the text value from something that doesn't provide updates synchronously from `onValueChange`, the text field starts behaving erratically; characters randomly disappear, the cursor moves around in its own, and other weird stuff.

This issue made us realize that mapping UI state on a background thread will never work. So we've been running our presenter composition on the main thread, using an immediate monotonic frame clock:
```kotlin
private object ImmediateMonotonicFrameClock : MonotonicFrameClock {
override suspend fun withFrameNanos(onFrame: (frameTimeNanos: Long) -> R): R {
return onFrame(System.nanoTime())
}
}
```
This completely solved the keyboard glitchiness.

We're now looking to migrate to Molecule. But in doing so, we've seen the keyboard glitchiness return!
I somewhat narrowed it down to behaviors that depend on the combination of the Molecule's `Dispatcher` and `MonotonicFrameClock`:
| Dispatcher | MonotonicFrameClock | Behavior |
| --- | --- | --- |
| `AndroidUiDispatcher` | `Choreographer` | Bad |
| `AndroidUiDispatcher` | `ImmediateMonotonicFrameClock` | Less bad (but still bad) |
| `Dispatchers.Main` | `Choreographer` | Bad |
| `Dispatchers.Main` | `ImmediateMonotonicFrameClock` | Good |

#### More specifics on the glitchiness:
Try spamming a character quickly. Occasionally, some will be dropped. When that happens, this appears in logcat:
```
getSurroundingText on inactive InputConnection
beginBatchEdit on inactive InputConnection
getTextBeforeCursor on inactive InputConnection
getTextAfterCursor on inactive InputConnection
getSelectedText on inactive InputConnection
endBatchEdit on inactive InputConnection
```
An easy way to check if the issue is happening is to hold the backspace button when there's some text present. With the issue, the backspace will randomly "stop working"; characters will stop being deleted even though you're still pressing backspace.

Honestly, I'm not positive as to whether this is Molecule's or `CoreTextField`'s fault. Input appreciated.

#### [Repro project with those dispatcher/frameclock modes](https://github.com/cashapp/molecule/files/7531556/MoleculeKeyboardScheduleBug.zip)

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with the linked reproduction project and compare the four Dispatcher and MonotonicFrameClock combinations described in the issue. Trace the interaction between Molecule and Compose UI's CoreTextField while reproducing dropped characters and inactive InputConnection messages. Done means identifying the responsible boundary and verifying that rapid typing and backspace no longer glitch.

Written by the indexing model from the issue text.

Assessment

Tech stack
kotlin
Domain
mobile-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.