6

I was implementing the outlinedTextField in android using the new compose library. But strangely the input data was not updated in the text field.

So I searched and found a topic called Recomposition in android compose. I didn't get it completely.
However, I did find the solution:

@Composable fun HelloContent(){ var name:String by remember {mutableStateOf("")} OutlinedTextField( value = name, onValueChange = {name = it}, label = {Text("Name")} ) } 

and I also read on the concept of State in jetpack compose. But I wasn't able to get it completely.
Can someone explain it in simple words?

3
  • Check out this youtube video which explains the basic principles. Commented Nov 5, 2021 at 13:58
  • Philip you are advertising here I'll flag you! Commented Nov 5, 2021 at 16:31
  • I flagged it Philip; You're going down Commented Nov 5, 2021 at 18:39

4 Answers 4

26

Basically, recomposition is just an event in Compose, in which the Composable in concern is re-executed. In declarative coding, which is what Compose is based on, we write UI as functions (or methods, more commonly). Now, a recomposition is basically an event in which the UI is re-emitted, by executing the body of the said Composable "function" all over again. This is what recomposition is, at its core. Now on to when it is triggered.

Ok, so in order to trigger recompositions, we need a special type of variable. This type is built into compose and was specifically designed to let it know when to recompose. And the mentioned type is MutableState. As the name suggests, it is State, that can Mutate, i.e., change; vary.

So, we have a variable of type MutableState, what's next? Guess what, you DON'T have a variable of type MutableState because I didn't teach you how to create one! The most common assignment you will use in Compose is the mutableStateOf helper. This is a pre-defined method that returns a value of type MutableState, well, MutableState<T>, actually. T is the type of State here, see below

var a = mutableStateOf(999)

Above, as you can see, 999 is an Int, and so, mutableStateOf here will return a MutableState<Int> type value. Easy enough.

Now, we have a MutableState<Int> value, but honestly, that's kinda ugly. Every time you need to get the value out of the MutableState<T>, you would need to refer to a property conveniently named .value.

So, to get the 999 out of the above var a, you would need to call a.value. Now, this is fine for use at one or two places but calling this every time seems like a mess. That is where Kotlin Property Delegation Come In (I did not need to Capitalize the last two words, I know). We use the by keyword to retrieve the value out of the state, and assign that to our variable - That's all you should care about.

So, var a by mutableStateOf(999) will actually return 999 of type Int, and not of type MutableState<Int>, but the brilliant part is that Compose will still know that the variable a is a State-Holder. So basically mutableStateOf can be thought of as a registering-counter, which you just need to pass through once, in order to get registered in the list of State-Holders. As of when, a recomposition will trigger every time the value of one of the state-holders is changed. This is the rough idea, but let's get technical; Now on to the "how" of recomposition.

To trigger a recomposition, all you need to ensure is two things:

  1. The Composable should be reading a variable, that is also a state-holder
  2. The state-holder should experience a change in its current value

Everything's better with Perry Examples:-

var a by mutableStateOf(999)

Case 1: A Composable receives a as a parameter value, MyComposable(a), then I run a = 0, Outcome 1: Recomposition Triggered

Case II: This declaration of variable a is actually inside a Composable itself, then I run a = 12344 Outcome II: Recomposition Triggered

Case III: I repeat cases 1 & II, but with a different variable, as follows: var b = 999
Outcome III: No Recompositions Triggered; Reason: b is not a state-holder

Great, we got the basics down now. So, this is the last phase of this lecture.

REMEMBER!!

You see when I say during recomposition, the entire Composable is re-executed, I mean the entire Composable is re-executed, that is, every single line and every single assignment, without exceptions. You see anything wrong with this yet? Lemme demonstrate

Let's say I want to have a Text Composable that displays a number, and increases that number when I click on it.

I could implement something as simple as this

@Composable fun CountingText(){ var n = mutableStateOf(0) //Starts at 0 Text( value = n.toString(), //The Composable only accepts strings, while n is of Int type modifier = Modifier .clickable { n++ } ) } 

Ok so this is the implementation that we might think would work. If you are unfamiliar with Modifiers, just leave that for now and trust me that it just triggers the code inside the clickable braces, when you actually click on the Text. Now, let's picture how this will be executed.

Firstly Compose will register the variable n as a state-holder. Then it will render the Text Composable with the initial value 0 of n.

Now, the Text is actually clicked. The block inside clicakble will be executed, which in this case is just n++, which will update the value of n. Compose sees that the value of n is updated, and runs through the list of state-holders. Compose finds that n is indeed a state-holder, and then decides to trigger a recomposition. Now, the entire Composable reading the value of n will be recomposed. In this case, that Composable is CountingText since a Text inside it is reading the value of n (To display it). Hence, CountingText will be "re-executed". Let's walk through the re-execution here.

First line in the Composable,

var n = mutableStateOf(0)

n became 0.

Next lines:-

Text( value = n.toString(), //Just displays 0 modifier = Modifier .clickable { n++ } //Just tells it to increase n upon click ) 

So you see, the catch here is that upon re-execution, n is completely created from scratch as if it never existed before. It was removed from the Composable's memory. To counter this, we need the Composable to remember n. That way, Compose knows that this is a state-holder AND holds a value that needs to be re-assigned to it upon recomposition. So, here's the updated first line (the rest is the same, just the initialization is updated)

var n by remember { mutableStateOf(0) }

Now, upon first execution, n will receive 0, since it is actually the very first time n is created. Thanks to remember, n now has access to the Composable's memory, and thus will be stored in the memory for future usage.

So, during recomposition, this is what happens - When the executor (???) reaches the line where n is assigned,

var n by remember { mutableStateOf(0) }

remember actually acts as a gatekeeper, and does not allow the executor to enter the block contained in it. Instead, it passes it the previously remembered value and asks it to move on. Since when the user clicked the Text, it already incremented the value of n to 1, this was retained in memory and so, now this works as expected.

This is the same case for your TextField problem. The field initially reads an empty value and the value is updated every time the user types a letter, triggering a recomposition and finally displaying the correct value on the screen.

Could it get simpler enough? Let me know I spent half an hour typing this.

Sign up to request clarification or add additional context in comments.

6 Comments

This is indeed the answer I expected. Just to add to this, while working with compose you may land up in error: Type 'TypeVariable(T)' has no method 'getValue(Nothing?, KProperty<*>)' and thus it cannot serve as a delegate Type 'TypeVariable(T)' has no method 'setValue(Nothing?, KProperty<*>, String)' and thus it cannot serve as a delegate for var (read-write property). If it is so just follow this answer.
Or the codelab for that matter. I have shared the link to this codelab with so many asker's here that I have literally memorized it now.
Just take the entire pathway you might get great help from that
You have a fan! I like the way you explain and kinda British humor, after I read this post I am seeing your previous post, please if you write a blog or book drop a line here quoting me so that I can be "reactively" notified
I am highly distant from social networks. The only apps for a social network that I use are StackOverflow, and YouTube (comments being the social part). I do like that you enjoyed my post here; keep up the enthusiasm for learning about the machine. I'm fascinated by it, like how 1s and 0s can make an inanimate object perform tasks previously exclusive to humans, that at the base of it all, it is probably just binary that we all exist in. Logic is binary, decisions are binary, scares the hell out of me to think that we may just be machine learning models, a complex aggregate of live bits
|
1

Recomposition is used in Compose to recompose (reload the parts that changed) the screen. In your example you have user input which changes the state of the screen. You have to use var name:String by remember {mutableStateOf("")}, by the way, you can leave the :String out because you set the type here: mutableStateOf("") anyway, you need to use remember that the composable remembers the old content and can then add the new content. If you type in h e l l o everytime you type one letter it recomposes, it remembers the value h, then he, then hel and so on.

Comments

0

Try this
I know there is not difference but leaving imports

Compose_version 1.0.4

import androidx.compose.material.OutlinedTextField import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.setValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @Composable fun YourAnswer() { var text by remember { mutableStateOf("") } OutlinedTextField( value = text, onValueChange = { text = it }, label = { Text("Name") } ) } 

Comments

0

Recomposition is updating the screen with ui changes.

Basically , we use remember to block unnecessary recomposition. Also we use mutableStateOf to trigger recomposition when value is changed. When they come together, we can manage the composition effectively. Also we use remember to do heavy calculation operation(especially Canvas library transactions). Additionally, dont forget it to use, if you are doing some lifecycle process(ex. changing device pose, triggers recomposition. We dont want to reset all.)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.