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

ursus

10/27/2023, 4:44 PM
Copy code
@Composable
fun MyScreen(viewModel: MyViewModel) {
    val state by viewModel.state.collectAsState()
    Scaffold {
        Column(
            modifier = Modifier.fillMaxSize()
        ) {
            Toolbar(
                titleRes = BR.string.main_title,
                onBackClick = viewModel::backClick
            )
            Text(state.title)
        }
    }
}
or
Copy code
@Composable
fun MyScreen(viewModel: MyViewModel) {
    Scaffold {
        Column(
            modifier = Modifier.fillMaxSize()
        ) {
            val state by viewModel.state.collectAsState()
            Toolbar(
                titleRes = BR.string.main_title,
                onBackClick = viewModel::backClick
            )
            Text(state.title)
        }
    }
}
is there any difference in terms of recomposition/performance between the two, as to where the
state
is collected? I read that when
state
changes the nearest composable parent is found and only that is recomposed, correct? So by that logic n.2 is superior, where it only recomposes the
Column & children
, right? Whereas the first one recomposes the whole screen (obviously most of it will be no-op, but still, it has to do the diffing, right? probably the performance is negligible, but help me establish a the correct pattern
a

Albert Chang

10/27/2023, 5:04 PM
In this specific case, there’s no difference in terms of performance because only the scope in which a state is read will be recomposed when the state changes.
collectAsState()
converts a flow into a state, but doesn’t read the state. That said, the general idea that putting a state higher in the hierarchy can potentially result in more recompositions is correct.
u

ursus

10/27/2023, 5:06 PM
okay so just to clarify, the algoritm will find the "highest" read, which is
state.title
and then look for a parent, which is the
˙Column
and recompose that?
a

Albert Chang

10/27/2023, 5:11 PM
Inline functions such as
Column
will not introduce a recompose scope. Read this post for more details.
u

ursus

10/27/2023, 5:12 PM
hmm interesting, why is it inline then? feels like a downside, no? Since now
Scaffold
needs to be recomposed
a

Albert Chang

10/27/2023, 5:18 PM
Calling composable functions isn’t free. Making some commonly used layout primitives inline can improve the performance of composition. I believe the decision is supported by data.
u

ursus

10/27/2023, 5:21 PM
So having my view as a ColumnScope extension, when already in Column at parent level. is useless?
a

Albert Chang

10/27/2023, 5:23 PM
What do you mean by “useless”?
If you mean your function call won’t introduce a recompose scope, you are wrong. Only the body of an inline function is inlined, and inline won’t spread. As long as your function is not inline, it will introduce a recompose scope.
x

xoangon

10/28/2023, 7:06 AM
> ...
collectAsState()
converts a flow into a state, but doesn’t read the state > If you have
val state = viewModel.state.collectAsState()
you're not reading the state, but if you use
by
then it does, as it resolves the
State<UiModel>
wrapper.
Using
val state by viewModel.state.collectAsState()
does read the state, so I would expect both code snippets to behave a little different in terms of recomposition:
If you read the state at the start of the function, the closest recomposition scope is
MyScreen
If you read the state inside
Scaffold
then the closest recomposition scope is the one of
Scaffold
I was wrong in this, thanks @Albert Chang for pointing out
a

Albert Chang

10/28/2023, 7:18 AM
No. The definition of a variable using property delegate doesn't read the state. The definition is essentialy the same as this (with the only difference being that property delegate can be used inside a function):
Copy code
val _state = viewModel.state.collectAsState()
val state
    get() = _state.value
That's how property delegate works.
x

xoangon

10/28/2023, 7:32 AM
You're right @Albert Chang, the type resolves the wrapper but it's read on variable call. I was wrong then