I have a tab bar composable. It "remembers" a sele...
# compose
t
I have a tab bar composable. It "remembers" a selectedIndex mutable state. I want to instrument the composable with an onSelect closure that fires when that selectedIndex gets updated. It's unclear to me how to add/register side effects to one of these remembered states. I tried SideAffect(), but that didn't seem to work 😞 I think I'm missing something obvious, or thinking about things upside down somehow. Looking for the light bulb here.
c
LaunchedEffect(selectedIndex) { }
is probably the easiest way. The block will get called anytime
selectedIndex
changes. The
SideEffect
function isn’t particularly useful for most things you’d want to do in Compose
t
So like this?
Copy code
var selectedIndex = remember { mutableStateOf(1) }
LaunchedEffect(selectedIndex) { onSelect(selectedIndex.value) }
?
c
You need to put as the key the value that is actually changing. The State variable itself does not change, but its
.value
does. So in this case, you’d do:
Copy code
val selectedIndex = remember { mutableStateOf(1) }
LaunchedEffect(selectedIndex.value) { onSelect(selectedIndex.value) }
It’s more common to use the property delegate for state variables though, which simplifies it a bit:
Copy code
var selectedIndex by remember { mutableStateOf(1) }
LaunchedEffect(selectedIndex) { onSelect(selectedIndex) }
t
Thank you. It confuses me. My brain sees the .value accessor is just returning a plain Int. So I don't see how it senses the Int changes. It seems like it should be the "Box" that holds something (.e.g. MutableState) that you "observe" for changes. Is the Compose compiler more magical/helpful than i realized?
c
The Compose compiler is incredibly magical, and does a lot of heavy lifting here. It’s a bit intuitive at first, but once you wrap your head around it, it becomes much easier to use, and much more pleasurable to write your UI compared to more traditional UI toolkits. The mental model is that the code you write reflects the current state of your application. The
mutableStateOf()
variables are what tell Compose when the “current state” has changed, which initiates “recomposition” that actually updates what’s visible on the screen. The compiler magic wires up those
mutableStateOf()
variables to help the Compose Runtime with that recomposition process. For the
mutableStateOf()
variables themself, it’s basically just a box that holds a value. It can be helpful to look at the actual types in this case, to understand the difference between the two snippets:
Copy code
val selectedIndex: MutableState<Int> = remember { mutableStateOf(1) }
Copy code
val selectedIndex: Int by remember { mutableStateOf(1) }
Using the
by
keyword is a Kotlin property delegate that simply calls
MutableState<T>.value
. And it’s inline, so theres literally no difference between the two, it’s just a simpler syntax
When when looking at how the State variable triggers the
LaunchedEffect
, consider what it means in the context of “the current state”. If you passed
MutableState<Int>
as the key of
LaunchedEffect
, nothing will ever happen because the box itself never changes. But as the value inside the box changes, the Compose recomposition detects the change and “recomposes”, at which point the
LaunchedEffect
can see that the current is not the same as the previous one, and then runs its block.
t
That helps a little. I'll keep trying to bend my brain to this way of thinking. 😄 Your explanation (and encouragement) helps.
c
Getting to think in terms of “current state” and understanding how these “keys” work is pretty crucial for working with Compose because it’s used in many different places (
remember(key) { }
, other types of effects). But like most things, the more you work with it, the more it will make sense, so it’s just a matter of playing around with the code and seeing how it behaves. The CodeLab does a great job of walking you through the basics and explaining what’s going on and how to do things properly
t
I did those a couple weeks back. Wish it had been fresher in my mind. Until you struggle to "do your own thing" it doesn't stick (for me at least)