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

Wajahat Karim

07/28/2020, 2:04 PM
I have been trying to play around
state
in Compose dev15. So, if create a state like
val numState by state { 0 }
, and use this in any composable like Text, it works good and fine. On doing
numState++
, the Text updates. But, if I use some custom data class like this below code:
data class Point(val x:Int = 0, val y:Int = 0)
And using this class to create state like
val pointState by state { Point(3,4) }
, and now if I update this like
pointState.x++
, then the Composables using this state aren't recomposed and updated. I don't understand how am I supposed to use state?
a

Adam Powell

07/28/2020, 2:08 PM
Only the state var itself is observable in the above, not the contents
w

Wajahat Karim

07/28/2020, 2:10 PM
Yes, when I did it like
var newPoint = pointState.copy(x = 3)
and reassiging this to
pointState = newPoint
, then it worked. But I am confused on whether this is a good practice, considering if contents are some ArrayLists etc instead of simple x, y integers performance wise?
a

Adam Powell

07/28/2020, 2:12 PM
It often depends. Persistent collections are a good way of keeping immutability while preserving scalable performance, e.g. https://github.com/Kotlin/kotlinx.collections.immutable
mutableStateListOf
for example creates a list backed by persistent collections
on the other side of things if you use
MutableState
for properties of mutable objects, you do get snapshot isolation for thread safety, plus observability, but it does come with overhead beyond a simple
Int
property; we're working to keep it cheap enough to use frequently but it's not free 🙂
w

Wajahat Karim

07/28/2020, 2:15 PM
So, instead of combining these lists in a wrapper data class, if I use simply
mutableStateListOf
and update it, then this will work?
Agree. I was actually looking into
MutableState
last night and will definitely experiment with it today to see if it solves my problem.
a

Adam Powell

07/28/2020, 2:16 PM
so your point example could become:
Copy code
@Stable
class Point(x: Int, y: Int) {
  var x by mutableStateOf(x)
  var y by mutableStateOf(y)
}

// usage; note 'remember' instead of 'state'
val point = remember { Point(1, 2) }
👍 1
t

than_

07/28/2020, 2:17 PM
There are tradeoffs (aren't there always? 🙂 ). Yes there is performance overhead on always creating new instances. But on the other hand you can easily check if the object changed without deep comparison. The performance overhead of creating new objects is mostly fine. Working with deep nested immutable data is unfortunately another beast 😕. For that I'd recommend this https://arrow-kt.io/docs/0.10/optics/dsl/
👍 4
a

Adam Powell

07/28/2020, 2:17 PM
yeah the arrow optics stuff is pretty slick. I don't think I'd recommend it to someone new to programming, but for someone comfortable with these concepts it's super handy
mutable data does, as always, introduce the opportunity for different parts of the code to modify it outside of your expectations, so it's still good to prefer immutability unless you have a strong reason to make something mutable
t

than_

07/28/2020, 2:20 PM
Oh for sure, there is a learning curve. But I believe its good (even for begginers) to know if there are solutions for your pain points 🙂
w

Wajahat Karim

07/28/2020, 2:20 PM
Yeah. Arrow is defintily trickier. I will try to understand it. Also, I was looking at a callback loop from this snippet of you @Adam Powell https://gist.github.com/adamp/e69593c28fa8642bd7eee22bb4a733ee I am unable to do this in dev15. Can you tell me how can I do this in dev15?
a

Adam Powell

07/28/2020, 2:21 PM
right. I mention it because I do feel strongly that compose is something I could put in front of a first time programmer by the time we hit 1.0; once you get into more complex data flows the learning curve gets steeper
👍 2
@Wajahat Karim what about it isn't working?
oh, the
awaitFrame
method was renamed to
withFrameMillis
iirc
w

Wajahat Karim

07/28/2020, 2:23 PM
The
awaitFrameMillis
and
lifecycleOwner
are unresolved variables
a

Adam Powell

07/28/2020, 2:26 PM
lifecycleOwner
is a local variable, the
whenStarted
extension comes from the
lifecycle-ktx
library
the
LifecycleOwnerAmbient
is still there, should just be an import
w

Wajahat Karim

07/28/2020, 2:30 PM
It looks like
awaitFrame
is deprecated in dev15.
a

Adam Powell

07/28/2020, 2:32 PM
you're looking for
withFrameMillis
w

Wajahat Karim

07/28/2020, 2:33 PM
Oh sorry, I got confused, You are right. Thanks a bunch 🙂
👍 1