This is the 7th day of my participation in the August Gwen Challenge.

An overview of

The so-called state can be simply understood as the change of a value in the application, such as a Boolean value, an array

In the business scene, it can be the text in the TextField, the state of animation execution, the user’s collection of goods are all states

We know that compose is a declarative UI, and that every time we reorganize the page, we reorganize the component, so we need to introduce state management, for example:

We click the button in the item of the commodity to collect the commodity, at this time, the collection state of the commodity changes, we need to restructure the UI to change the commodity into the collection state, at this time, we need to use the remember extension method to save the reorganization state. If you use the Boolean primitive, you will not be able to set the state of the component properly after reorganizing the UI.

Code examples (copy official code) :

@Composable
fun HelloContent(a) {
   Column(modifier = Modifier.padding(16.dp)) {
       OutlinedTextField(
           value = "Input value",
           onValueChange = { },
           label = { Text("Name")})}}Copy the code

Running the above code, we can see that the contents of the TextFile do not change no matter how we enter the contents in the TextField. This is because we cannot save the state. The following code example can change the contents of the TextField normally


@Composable
fun textFieldStateHasTextShow(a){
    var value by remember {// Save the state of the text displayed in the TextField
        mutableStateOf("")
    }
    Box(modifier = Modifier.fillMaxSize(1f),contentAlignment = Alignment.Center) {
        OutlinedTextField(
            value = value,
            onValueChange = {
                  value=it// The updated state is called back each time the content is entered to refresh and reorganize the UI
            },
            label = { Text("Name")})}}Copy the code

Common methods of state management

Remember to preserve state during reassembly

Composition functions can remember individual objects by using Remember, and the system stores the value remembered initially in the composition during initialization. Remember can be used to store both mutable and immutable objects

When the composable item is removed, the object that Remember stored is forgotten.

mutableStateOf

MutableStateOf creates observable MutableState

, for example: Data is a MutableState object

Whenever the data.value value changes, the system reorganizes the UI.

var data = remember {
        mutableStateOf("")}Copy the code

Note: mutableStateOf must use the Remember nesting to reorganize the interface when data changes

RememberSaveable Save the configuration

Remember helps us store state when the interface is reorganized, while rememberSaveable helps us store state when configuration changes (recreating an activity or process).

Livedata, Flow, and RxJava convert to state

These three frameworks are the three commonly used android responsive development frameworks, and all support converting State objects. Take Flow as an example, the following code can convert a State:

  val favorites = MutableStateFlow<Set<String>>(setOf())
    val state = favorites.collectAsState()
Copy the code

State management

Stateful and stateless

A combination that saves state using the Remember, rememberSaveState methods is a stateful combination

Otherwise, it is stateless combination

State of ascension

The following code is the official code for status promotion:

In this example, HelloContent is stateless and its state is promoted to HelloScreen. HelloContent has two parameters: Name and onNameChange. Name is the state. Pass to HelloContent via the HelloScreen composition

Changes that occur in HelloContent cannot be handled by itself, and must be passed to HelloScreen for processing and reorganizing the interface.

This logic is called: states go down, events go up

@Composable
fun HelloScreen() {
    var name by rememberSaveable { mutableStateOf("") }

    HelloContent(name = name, onNameChange = { name = it })
}

@Composable
fun HelloContent(name: String, onNameChange: (String) -> Unit) {
    Column(modifier = Modifier.padding(16.dp)) {
        Text(
            text = "Hello, $name",
            modifier = Modifier.padding(bottom = 8.dp),
            style = MaterialTheme.typography.h5
        )
        OutlinedTextField(
            value = name,
            onValueChange = onNameChange,
            label = { Text("Name") }
        )
    }
}
Copy the code

How to store state

Remember from the rememberSaveable method that you can save a state in a Bundle. What if the rememberSaveable state is not available in a Bundle?

There are three ways to save non-bundle data (save after configuration changes)

Parcelize

Code examples:

@Parcelize
data class City(val name: String, val country: String) : Parcelable

@Composable
fun CityScreen(a) {
    var selectedCity = rememberSaveable {
        mutableStateOf(City("Madrid"."Spain"))}}Copy the code

MapSaver

data class City(val name: String, val country: String)

val CitySaver = run {
    val nameKey = "Name"
    val countryKey = "Country"
    mapSaver(
        save = { mapOf(nameKey to it.name, countryKey to it.country) },
        restore = { City(it[nameKey] as String, it[countryKey] as String) }
    )
}

@Composable
fun CityScreen(a) {
    var selectedCity = rememberSaveable(stateSaver = CitySaver) {
        mutableStateOf(City("Madrid"."Spain"))}}Copy the code

ListSaver

data class City(val name: String, val country: String)

val CitySaver = listSaver<City, Any>(
    save = { listOf(it.name, it.country) },// The values stored in the array correspond sequentially to the properties in City
    restore = { City(it[0] as String, it[1] as String) }
)

@Composable
fun CityScreen(a) {
    var selectedCity = rememberSaveable(stateSaver = CitySaver) {
        mutableStateOf(City("Madrid"."Spain"))}}Copy the code

State management source code analysis

remember

Remember for the first time read the source code, there may be a wrong understanding of the place (but someone has to go to see not), forgive me, welcome correction

  • The main flow of the remember method call

The Remember method returns a MutableState object, which notifies the system to reorganize the UI when data is updated

RememberedValue is the logic for data transformation

  • RememberedValue method resolution

Inserting: If we are inserting new nodes into the view number, inserting=true

Reusing: means being reused. My understanding is that the state is currently being reused, so avoid multiple fetches

  • The reader.next method shares a piece of source code
  fun next(): Any? {
        if (emptyCount > 0 || currentSlot >= currentSlotEnd) return Composer.Empty
        return slots[currentSlot++]
    }
Copy the code

The currentSlot is the index of the state we want to get in the array. The compose build page is single threaded. The index gets from slots every time we call remember if the state already exists. Then we increment the currentSlot index by 1, so that when we call the last remember method the currentSlot index is exactly equal to the slots array.length-1