Hi! :wave: Has anyone here used `TextFieldState` i...
# compose
w
Hi! 👋 Has anyone here used
TextFieldState
inside a
ViewModel
? I’ve seen it suggested in the docs (e.g., here and here), but I’m not fully convinced — especially because it makes testing harder. To keep things simple, here’s a test case (imagine that
TextFieldState
and
snapshotFlow
are part of the ViewModel):
Copy code
@Test
fun `when text changed, then update the text`() = runTest {
    val state = TextFieldState()
    snapshotFlow { state.text.toString() }
        .test {
            assertEquals("", awaitItem())

            state.setTextAndPlaceCursorAtEnd("test")

            assertEquals("test", awaitItem())
        }
}
This test fails with a timeout:
Copy code
No value produced in 3s
app.cash.turbine.TurbineAssertionError: No value produced in 3s
I found that wrapping the text change with
Snapshot.withMutableSnapshot
makes it work:
Copy code
Snapshot.withMutableSnapshot { state.setTextAndPlaceCursorAtEnd("test") }
…but I’m still not convinced this is a good approach overall. Has anyone dealt with this? Would love to hear your thoughts.
s
Changing the cursor position should not change the
state.text
value, therefore you are not getting a new emission to
awaitItem()
on.
w
but I'm changing the text
setTextAndPlaceCursorAtEnd
😅
s
Not sure what's the best way but I've been using
Snapshot.sendApplyNotifications()
instead. Relevant thread: https://kotlinlang.slack.com/archives/CJLTWPH7S/p1671685168586409
👍 1
s
but I'm changing the text
setTextAndPlaceCursorAtEnd
😅
I saw the "cursor" part of the function and my brain simply stopped reading after that it seems like, idk wtf I was thinking when I answered this 😂
blob no problem 1
h
We are actively looking into how we can make
TextFieldState
more testable. Even if we did not recommend
TextFieldState
in ViewModels, people are going to do it. So the testing of it depending on
snapshotFlow
and applying change notifications is not ideal. Hopefully we will have more to share soon.
👍 2
qq; why are you using
snapshotFlow
in this case and why are you testing
TextFieldState
's own functionality?
w
It's just a reproduction sample, I didn't want to include too much code 😄
h
Fair, but I would like to know why you needed this behavior so we can have that under consideration.
w
My original scenario is that I observe user input in one part of the ViewModel using something like:
Copy code
snapshotFlow { stateValue }
    .onEach { /* fetch something from API */ }
    .launchIn(viewModelScope)
In a different function, I programmatically update the text using
setTextAndPlaceCursorAtEnd
. If I didn’t need to set the text manually, I would move the
snapshotFlow
into the Composable and just notify the ViewModel when the text changes. But since I need to update the text from the ViewModel too, it gets a bit more complicated.
s
> We are actively looking into how we can make
TextFieldState
more testable. this would be superb! we recently introduced a wrapper at cash app for hoisting text inputs in our presenters, but having something official would be better!
h
@Wojciech Krystyniak are you changing the TextFieldState according to changes you observe from it? Why not
InputTransformation
?
w
I have an autocomplete component, you can compare it to e.g. youtube search - as you type, the list gets updated, but you can also click the suggestion and the text is updated. Attaching a sample code to illustrate it better
👍 1
a
@Halil Ozercan
Even if we did not recommend
TextFieldState
in ViewModels
Why is that? Is it simply because the difficulty of testing? I think it's a common pattern because the only way to keep a single source of truth is to put
TextFieldState
(as part of UI state) in the view model.
h
I was just being hypothetical. The scenario I was describing was that even if
TextFieldState
in ViewModel wasn't the endorsed way, we would still need to make sure that it is not difficult to test.