One Off the Slack

These articles summarize the discussion on a particular thread in the #compose channel of JetBrains’ Slack workspace for Kotlin developers. If you would like to read the original thread, you will need access to that workspace — sign up at https://slack.kotlinlang.org!

Do We Need State To Remember?

Cafer Mert Ceyhan asked:

According to the rememberSaveable’s documentation, it says: “If you use it with types which can be stored inside the Bundle then it will be saved and restored automatically using autoSaver, …“. So, I was expecting this code can survive during the configuration changes but it doesn’t. Am I missing something?

var foo = rememberSaveable { false }

Csaba Szugyiczki wondered:

how do you change the value of foo?

I think you need to us it like this, otherwise you will change the value of the variable foo, and not the value backed in the rememberSaveable

var foo by rememberSaveable { mutableStateOf(false) }

Cafer then elaborated:

Yes if I wrap it with mutableStateOf() it works but I don’t need the State because of I don’t need recomposition. I want to save this variable during the configuration changes.

var foo = rememberSaveable { false }

LaunchedEffect(Unit) {
    if (!foo) {
        foo = true
    }
}

Csaba then explained more about the problem:

you are saving false and reading false all the time. Inside the LaunchedEffect what happens is the foo variable gets the value of true, but that does not change the originally saved value which is false

Maybe you can create a reference object that you can modify without triggering a recomposition, and save the value stored in that like in this article: https://www.jetpackcompose.app/articles/how-can-I-debug-recompositions-in-jetpack-compose#printing-log-statements

But I cant see why you would need anything like this. This side effect seems to be way more complicated than it should usually be

Cafer agreed:

I see the problem, saying foo = true doesn’t change the saved value, right? It makes sense, looks like I should use mutableStateOf() even if I don’t need it. It is easy solution.

Google’s Ian Lake then chimed in to help clarify what is really going on here:

mutableStateOf() creates a MutableState object - a recomposition aware wrapper around a set and get. While you may not need the recomposition aware part, what rememberSaveable is capturing is that wrapper object - that’s why when the autoSaver calls get, it gets the latest value you’ve set

Any wrapper would do, it just happens to be that you get a prebuilt one for you in Compose land 🙂

And, when Cafer asked if Ian recommended mutableStateOf() even if recompositions are not needed, Ian said:

Just use mutableStateOf(), then if your requirements change and you do need the recomposition part, you’ll get that for free

Posted 2022-06-19, based on https://kotlinlang.slack.com/archives/CJLTWPH7S/p1654699973495049


When Do We Enter, and When Do We Leave?

Dmitry Suzdalev asked:

When we say that something “enters the composition” or “leaves the composition”, what does this mean exactly? I noticed that I’ve started to think about @Composable functions as objects in this regard, but that’s not true, is it? A function itself doesn’t have anything to do with composition lifecycle: only its effects do. Is there some good mental model to think about this?

Stylianos Gakis tried to explain:

If you have some code that looks like:

var someVar: Boolean
if (someVar) {
    MyComposable()
}

When someVar becomes false, the MyComposable leaves composition as it no longer is shown. remembers inside the composable function itself get destroyed etc.

When it becomes true again, MyComposable gets put back into the slot table, therefore all of its LaunchedEffect(Unit) etc. are run. It “enters composition”.

This is the first thought that comes to my mind when you say all this, is this what you’re looking for? If not please ask away. I do not know what you mean exactly by saying that you’re thinking of them as objects.

Dmitry was… less than comfortable with that explanation:

you say “it no longer is shown”: what is “it” here? A composable function? But function is not shown, it’s an entity of the programming language 🙂 This is what I mean when I say I start to think about them as “objects”.

I understand how it works, but I wonder if this can be formulated better from a structural point of view, when explaining things to someone. Or do we have to explain slot table whenever we want to deepen the understanding? Maybe there’s something in-between.

Stylianos understood the frustration with terminology:

You’re right that “shown” is not the right verb for this, I agree.

I find it the easiest to think as you said exactly, it leaves the slot table, therefore it no longer exists. The function no longer emits the UI it would emit if it was present in the slot table.

And you’re right that a function is not shown. I like to think of it as a functionemits UI, as that is what Unit returning composable functions do.

Dmitry agreed:

That’s mostly how I came to think about this too. And if we store comosable functions in the map or graph (like in navigation library), this doesn’t mean that we store “components”, we actually store “recipes” (?) to produce those components.

Then, Google’s Zach Klippenstein weighed in:

You can also describe it a bit more concretely as “a composable is present if it was called in the last composition pass/frame¹, and it is not present if it was not called in the last composition pass/frame.”

¹actually the last composition that was actually applied, but I’m guessing if you’re still trying to wrap your head around presence that distinction isn’t helpful

Posted 2022-06-12, based on https://kotlinlang.slack.com/archives/CJLTWPH7S/p1654518481583399


Where Did My setContent() Go?

Henrique Horbovyi asked:

I’m trying to setup compose in an existing project and I’m facing the following error: NoSuchMethod: No Static method setContent

My dependencies:

dependencies {
    implementation("androidx.core:core-ktx:1.7.0")
    implementation("androidx.compose.ui:ui:1.1.1")
    implementation("androidx.compose.material:material:1.1.1")
    implementation("androidx.compose.ui:ui-tooling-preview:1.1.1")
    implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.4.1")
    implementation("androidx.activity:activity-compose:1.4.0")

    debugImplementation("androidx.compose.ui:ui-tooling:1.1.1")
    debugImplementation("androidx.compose.ui:ui-test-manifest:1.1.1")
}

Google’s Adam Powell offered:

You’re not running the compose compiler plugin in the module that includes the call to setContent

If you are loading either the Kotlin or Android Gradle plugins but not both in your buildSrc then this can also happen for some non-obvious gradle classpath reasons

if you load either of these plugins inside your buildSrc

dependencies {
    implementation("com.android.tools.build:gradle:$androidGradlePluginVersion")
    implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinGradlePluginVersion")

then you must load both of them together

Posted 2022-06-05, based on https://kotlinlang.slack.com/archives/CJLTWPH7S/p1654094194207519


More!

Older “One Off the Slack” articles can be found in the archives.