Ayfri
07/12/2021, 8:01 AMMitchell Skaggs
07/12/2021, 8:09 AMremember
a MutableState
of the color
2. Use the state's value to set the button color (see this example: https://developer.android.com/jetpack/compose/themes?hl=en#component-styles)
3. Modify the state's value in the onClick
function of the button.
Jetpack Compose takes care of the update propagation redrawing, etc.Ayfri
07/12/2021, 8:10 AMonClick
function ? how ?Mitchell Skaggs
07/12/2021, 8:11 AMMutableState
that the button reads. That's what allows Jetpack Compose to "know" you modified something and "tell" the button that something changed (to recompose).Ayfri
07/12/2021, 8:13 AMTask
component right now, it's for a todo application where I have a todo
MutableState value which contains Todo
instances, and in my App I do a todo.forEach { Task(it) }
but I guess that it's not the right way to do it ? It's my first Compose app (I literally started 30 minutes ago)Mitchell Skaggs
07/12/2021, 8:16 AMTodo
instances? In that case, you have to adopt some kind of bigger state management. Redux and the like are common patterns currently. (Shameless self-promotion, I'm developing a different state management solution that's based on functional programming if that's up your alley: https://github.com/magneticflux-/jetpack-compose-optics/tree/40142514e00a2fac94ecde802121148fcee6a522/example/src/main/kotlin/com/skaggsm/compose)Mitchell Skaggs
07/12/2021, 8:17 AMTodo
object, like a validation state or something)Ayfri
07/12/2021, 8:29 AMdata class Todo(val task: String) {
var state = TodoState.TO_DO
}
enum class TodoState(val color: Color) {
DONE(Color.Green),
IN_PROGRESS(Color(150, 200, 220)),
TO_DO(Color(200, 200, 200))
}
And my component is for now
@Composable
fun Task(todo: Todo) {
Button(
colors = ButtonDefaults.buttonColors(todo.state.color),
onClick = {
todo.state = TodoState.DONE
}
) {
Text(todo.task)
}
}
How should I update there the color whenever the task changed its state ?Mitchell Skaggs
07/12/2021, 8:37 AMequals
and hashcode
for your Todo
class now since you put a mutable property in it and that's how Compose detects changes.Ayfri
07/12/2021, 8:38 AMMitchell Skaggs
07/12/2021, 8:39 AMdata
classes only include the constructor parameters. You should also read this caveat about using mutable objects: https://developer.android.com/jetpack/compose/state#use-other-types-of-state-in-jetpack-compose
And about data
classes: https://kotlinlang.org/docs/data-classes.html
The compiler automatically derives the following members from all properties declared in the primary constructor:
Mitchell Skaggs
07/12/2021, 8:49 AMAyfri
07/12/2021, 8:50 AMAyfri
07/12/2021, 8:50 AMTobias Suchalla
07/12/2021, 9:01 AMdata class Todo(val task: String) {
var state by mutableStateOf(TodoState.TO_DO)
}
@Composable
fun Task(todo: Todo) {
Button(
colors = ButtonDefaults.buttonColors(todo.state.color),
onClick = {
todo.state = TodoState.DONE
}
) {
Text(todo.task)
}
}
This might alreday be enough to get you started (notice the mutableStateOf in the data class). But as Mitchell mentioned, you should hoist your state, i.e. not have your composable mutate the state directly but have the caller do that. Compose is somewhat confusing with its state model at the beginning, but it get's better!Ayfri
07/12/2021, 9:03 AMZach Klippenstein (he/him) [MOD]
07/12/2021, 3:09 PMRick Regan
07/12/2021, 9:19 PMYou have to implement equals and hashcode for your Todo class now since you put a mutable property in it and that's how Compose detects changes.I don't understand the "that's how Compose detects changes" part of that. The code that Tobias has written works without implementing those methods, right?
Ayfri
07/12/2021, 9:22 PMMitchell Skaggs
07/12/2021, 11:28 PMequals
method on the previous and new objects. If they are the equal, a change is not dispatched.
The missing equals
method is only one issue with that code; the main issue is that the class is mutable, so Compose is unable to compare previous values (because they were mutated instead of replaced). For more info, see the caveat I mentioned above: https://developer.android.com/jetpack/compose/state#use-other-types-of-state-in-jetpack-compose
Tobias's code uses a MutableState
inside the data class, so it compares the TodoState
enums, not the entire data class. The enums have equals
implemented and are immutable, so they work correctly. This isn't recommended, however, because it couples your data types to Compose specifically and means that your data types are mutable, which is undesirable in more complex applications.Rick Regan
07/13/2021, 2:16 PMclass MyState(data: MyData, ...) {
var data by mutableStateOf(data)
...
}
This triggers recomposition when the variable data
(the reference) changes. I think what you're saying is that without an overriding equals
and hash
for MyData
, there won't be a structural equality check, which would lead to unnecessary (but harmless if they're idempotent) recompositions if the objects are structurally equal.
Also, I think I understand your point about immutability to a point, but it seems that since you have to put a mutableStateOf()
at some level, you're going to have a mutable object.