I don't know what is it
in Textfield's onValueChange using Jetpack compose.
val usernameState = rememberSaveable { mutableStateOf(TextFieldValue())}
TextField(
value = usernameState.value,
onValueChange = {usernameState.value = it}
)
Is it
the argument in lambda function?
I think every time the textfield's value changes, the lambda function is called.
So if we don't use the omission using it
, what do we write in the lambda function.
Please teach me. Thank you.
CodePudding user response:
In kotlin you can omit the argument of any lambda, after which you can refer to the argument with it
onValueChange = {usernameState.value = it}
is identical to
onValueChange = {value -> usernameState.value = value}
This of course only works for lambdas with a single argument.
CodePudding user response:
State hoisting
State hoisting in Compose is a pattern of moving state to a composable's caller to make a composable stateless. The general pattern for state hoisting in Jetpack Compose is to replace the state variable with two parameters:
value: T: the current value to display onValueChange: (T) -> Unit: an event that requests the value to change, where T is the proposed new value
However, you are not limited to onValueChange. If more specific events are appropriate for the composable you should define them using lambdas like ExpandingCard does with onExpand and onCollapse.
State that is hoisted this way has some important properties:
Single source of truth: By moving state instead of duplicating it, we're ensuring there's only one source of truth. This helps avoid bugs. Encapsulated: Only stateful composables will be able to modify their state. It's completely internal.
Shareable: Hoisted state can be shared with multiple composables. Say we wanted to name in a different composable, hoisting would allow us to do that.
Interceptable: callers to the stateless composables can decide to ignore or modify events before changing the state.
Decoupled: the state for the stateless ExpandingCard may be stored anywhere. For example, it's now possible to move name into a ViewModel.
In the example case, you extract the name and the onValueChange out of HelloContent and move them up the tree to a HelloScreen composable that calls HelloContent.
@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") }
)
}
}
By hoisting the state out of HelloContent, it's easier to reason about the composable, reuse it in different situations, and test. HelloContent is decoupled from how its state is stored. Decoupling means that if you modify or replace HelloScreen, you don't have to change how HelloContent is implemented.
The pattern where the state goes down, and events go up is called a unidirectional data flow. In this case, the state goes down from HelloScreen to HelloContent and events go up from HelloContent to HelloScreen. By following unidirectional data flow, you can decouple composables that display state in the UI from the parts of your app that store and change state.
**Key Point:** When hoisting state, there are three rules to help you figure out where state should go:
State should be hoisted to at least the **lowest common parent** of all composables that use the state (read).
State should be hoisted to at least the **highest level it may be changed** (write).
If **two states change in response to the same events** they should be **hoisted together**.
You can hoist state higher than these rules require, but underhoisting state will make it difficult or impossible to follow unidirectional data flow.