Typing into a text field in a lazy column is laggy...
# compose
c
Typing into a text field in a lazy column is laggy (even in the state codelab where it does this) Is that an issue or is there something we can do to fix this?
a
If you're on Linux with multiple monitors, it could be related to this issue: https://github.com/JetBrains/compose-jb/issues/23 If not then I have no idea blob shrug
c
Not desktop. Compose on android.
s
Responded on the ticket, not sure it was you who created the ticket @Colton Idle
c
Wasn't me but thanks! @Siyamed so the solution is to have an extra piece of state?
s
That is what it looks like
Solution not in the sense of workaround but what it should be
c
Hm. That is odd to me because it goes against the whole single source of truth? But what do I know. I've been doing Compose for like 6 hours tops. Lol
s
you can one state for the whole app :) that would be single source of truth as well :)
I currently cant speak for the the specific code, but in general each of the TextFields should have their iwn state
I believe there is something wrong with the codelab
The recreation of the list was suspicious
j
Errr, I think @Colton Idle is right, unless I'm miss-reading this, comment #3 in the issue seems like a huge antipattern and is very much not the right solution.
I agree with @Siyamed though that the
todoItems.toMutableList().also {
looks very suspicious and probably that codelab needs some additional review.
s
me reading the code as “each TextField has their own state” caused me to mislead. I strikethrough’ed it 🙂
@jim having a listOf<MutableState> instead of mutableStateOf<List> is not an antipattern is it correct?
j
listOf<MutableState>
is probably wrong, the user probably wanted
mutableStateListOf
instead.
mutableStateOf<List>
could be ok in some circumstances, but it has a very different behavior from
mutableStateListOf
👍 1
s
One question :)
MutableStateListOf is a mutable list right? So it would work well when we want to add and remove items
👌 1
Is it a good thing for a list of textfields that has their own independent state?
I was wondering the drawbacks of - create an array of 10 for 10 person names - each array element is data for a textfield - create lazycolumn for those
I would initially write this with an array of mutable states
I was wondering if that is wrong
j
I'm not sure I understand the questions. If there are ten people and I want to use mutable objects, I'd probably have something like a
mutableStateListOf<Person>
with each person defined as:
Copy code
class Person {
   var name by mutableStateOf<String>()
}
👍 1
If you could post a code snippet of the pattern you're imagining, maybe I can better comment on it. But as a general rule of thumb, probably mutableState should only be used as a property delegate backing a field or in a remember block. I'm not 100% confident enough to say that's a hard-and-fast rule yet (would love to see counterexamples) but that seems to be true off the top of my head without having thought too deeply about it.
s
The person example was good
Thank you
c
So it seems like that codelab will need to be updated? @jim glad I was thinking correctly about the anti pattern in the comment of having multiple pieces of state. In your person example above... Is the person able to back a text field properly? So basically as long as I have mutableStateListOf<Person> in my ViewModel and class Person { var name by mutableStateOf<String>() } Then I would just pass person.name to the text field? And everything should work right? Sorry on mobile or else I'd try but I'm really curious in making sure I understand this correctly. I have a use case of building a dynamic form (think a Google form) and so I want to use a LazyColumn (for best perf) and so I want to make sure I only have a single piece of state.
j
Copy code
LazyColumn(...) { index, person ->
    TextField(value=person.value, onChange={person.name = it})
}
c
@jim was able to get to a desktop and verify that edit texts in a lazy work fine. Thanks @jim no duplication of state either!
Copy code
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val listOfPeeps = mutableStateListOf<Person>()

        listOfPeeps.add(Person())
        listOfPeeps.add(Person())
        listOfPeeps.add(Person())

        setContent {
            ComposeTextFieldTheme {
                Surface(color = MaterialTheme.colors.background) {
                    LazyColumn(content = {
                        item {
                            Button(onClick = { listOfPeeps.add(Person()) }) {
                                Text(text = "Add person to list")
                            }
                        }
                        items(listOfPeeps) { person: Person ->
                            TextField(value = person.name, onValueChange = { person.name = it })
                        }
                    })
                }
            }
        }
    }
}

class Person {
    var name by mutableStateOf<String>("")
}
If you're still watching this thread, one thing I'm still not 100% sure about is why I need
Copy code
var name by mutableStateOf<String>("")
Couldn't I just have a var name = "" since the list is
mutableStateListOf
?
j
The
mutableStateList
only notifies observers when the LIST changes, not when an item within the list is mutated. If you want to be notified when the item within the list changes, that item must be observable (via state).
c
gotcha. makes sense. still learning, but yeah. this was pretty simple in the end to get working as intended.
unfortunately the codelab threw me off.
j
If the codelab threw you off, please file a bug with the section that threw you off and suggested updates to make it less misleading.
c
@Siyamed linked to someone else that also had codelab trouble. I'll star it though. 😁
@jim "The 
mutableStateList
 only notifies observers when the LIST changes, not when an item within the list is mutated.  If you want to be notified when the item within the list changes, that item must be observable (via state)." So something that is messing with me here is that I've been really hammering home the point that everything should be immutable if it can be. Especially in a kotlin data class like Person. Doing mutableStateOf<String> "feels" wrong. Is my person still following the "rule" of favoring immutablilty when possible?
j
You could make your person be a:
Copy code
data class Person(val name: String)
And just re-create the person if the name changes. That would be immutable.
You could also have a
mutableStateListOf<String>()
, as String is immutable so you don't need to wrap it in a
mutableStateOf
and can just remove and add strings as needed (although that gives you less room to grow in the future).
But no, if you are using any sort of
mutableState
then you are not favoring immutability.
c
Interesting. Still learning and I'm not sure this makes 100% sense yet, but I think it's clicking. Really the thing I'm trying to wrap my head around is if I needed a list of 10000 TextFields, how I would model that. Arguing with myself if
Copy code
data class Person(val name: String)
is better, or
Copy code
class Person {
   var name by mutableStateOf<String>()
}
j
Which one is better probably depends more on the rest of your app and data model, than a hard and fast rule. But the rule of thumb is to favor immutability, so maybe I'd go with the first one? Or maybe it's a coin toss.
c
@jim aha. Okay. I didn't know the first option was still "okay". I thought I "had" to use the second option in order for a list of TextFields to work. I will have to try this in a sample that isn't the Compose State codelab. I updated the codelab to use the mutableStateListOf and I was still seeing laggy typing, and so I thought the codelab was completely wrong since it didn't use
var name by mutableStateOf<String>()