https://kotlinlang.org logo
#compose
Title
# compose
m

Mikael Alfredsson

06/25/2020, 9:47 AM
I’ve must have missed something about how the UI updates are handled. Before (when the magic of @Model existed) I could change the data model outside a @compose scope (data pushed via FCM) and it automatically updated the UI. Without that magic, I should according to the migration guide, I should use
by state
or
mutableStateOf
together with
remember
, both of them reassign the datamodel to a wrapped variable inside the @Compose scope and i’m not sure how to mutate the data class and force a refresh.
m

mon

06/25/2020, 9:58 AM
It's the same as before, but instead of annotating the class owning the fields, you change the fields to
MutableState
delegates. So before if you had
var foo = Foo()
in your
@Model
class, you need to change that to
var foo by mutableStateOf(Foo())
. Then you use this class the same way as before.
state
is a shortcut to
remember { mutableStateOf(e) }
and
remember
is only meaningful in a
@Composable
so you can't use that delegate in a model class.
m

Mikael Alfredsson

06/25/2020, 10:06 AM
I guess i have to change from
Copy code
@Model
data class Model(
  var loading: Boolean = true
)
to
Copy code
class Model(
  loading: Boolean = true
) {
  var loading by mutableStateOf(loading)
}
Which (if this is correct) makes data classes unsuitable for compose models?
m

mon

06/25/2020, 10:14 AM
Yeah, I'd say so
You can change the type in the data class to
MutableState<Boolean>
but the usage would become weird
g

gildor

06/25/2020, 10:39 AM
makes data classes unsuitable for compose models
Mutable data class already not the best choice in general
1
m

Mikael Alfredsson

06/25/2020, 10:50 AM
True, but it just felt like I had to add a lot more plumbing code (which is true, but maybe for a good reason)
g

gildor

06/25/2020, 11:10 AM
maybe this would be better:
Copy code
class Model {
  var loading by mutableStateOf(false)
}

fun someInit(model: Model) {
   model.apply {
       loading = true
   }
}
I mean to keep mutable model a bit more simple with default values and instead populate it with some initial state
m

Mikael Alfredsson

06/25/2020, 11:12 AM
you’re right, most of the time there is a default that you can fall back to, and if not, you can always
apply
the changes directly when you create the instance the first time.
a

Adrian Blanco

06/25/2020, 12:16 PM
Otherwise if this is state from outside, the most natural thing might just be to just send down a Flow or equivalent with the data and then collect in the composition with collectAsState
g

gildor

06/25/2020, 1:46 PM
Yes, to populate it definitely better, but to update this state you need quite different unidirectional data flow architecture, which quite a different thing
l

Leland Richardson [G]

06/25/2020, 4:53 PM
fwiw, an @Model data class was already pretty weird in the first place. data classes still work great with compose in general, and promote immutable types (which we like). If you want to use data classes i encourage it, but I would hold on to them in a
State<MyDataClass>
and then instead of mutating the data class, you would use something like
.copy(…)
to produce a new one and assign the state object to the new value
it is true though that the property delegate syntax is more cumbersome in kotlin especially if you want that property to be a property in the primary constructor. This is unfortunate and i wonder if we should create a KEEP to enable some syntax like
Copy code
class Foo(val bar by someDelegate(bar))
but for the moment, the verbose-ish transformation you put in the beginning of the thread is the most direct way to update your code from @Model without changing the meaning of the code too much, but I would encourage you to instead consider a bigger refactor that moves towards immutable data classes held in state objects instead (this alternative was also spelled out in the release notes fwiw)
m

Mikael Alfredsson

06/26/2020, 8:25 AM
If I move them to state objects, wont they be “locked” to the @compose scope again, making it harder to update them from the “outside”, like from FCM? I still think the documentation are missing these kind of examples. Simple case, The Activity starts, sets the content, opens a Viewmodel that loads the data and then I want the new data to update specific parts of the view. I might have missed these examples, but normally I find either “click a button to update a field” or ” this is a full app that you can play with” and few things in between.
l

Leland Richardson [G]

06/26/2020, 4:50 PM
Copy code
val x = mutableStateOf<Thing>(someThing)
Right now there is extra work you need to do in order to assign x.value outside of the main thread, but there is nothing about
x
that says a composition has to exist or that it has to do with composition at all. If you want to assign it outside of the main thread, right now you will need to use
FrameManager.framed { x.value = newValue }
BUT, this is changing. We just landed a big refactor to the way all of this works and now
FrameManager.framed
is not needed and you can assign
x.value
in any thread at any time. You can see the change here: https://android-review.googlesource.com/c/platform/frameworks/support/+/1326201 You may be a little bit confused because
state { someThing }
is a composable call and can only happen inside of a composition, but
mutableStateOf
has no such constraint