I really like using the by
delegate with Compose State as it hides the boiler plate of having "reactive" data ie:
val message: String by remember { mutableStateOf("") }
message = "No boilerplate"
When using props this way, unidirectional data flow (or props down, events up) is clean and easy.
However when wanting to group data together in a data class
, or observe a view model prop as state (ie: using observeAsAState()
) I have to declare the prop of type State
eg:
data class MyUiProps(
val message: State<String>
)
var props = MyUiProps(viewModel.message.observesAsState(""))
props = MyUiProps(remember { mutableStateOf("") })
props.message.value = "Urgh boilerplate"
Is there a way to still use the by
delegate with a data class like the following pseudo code
data class MyUiProps(
val message: String
)
val props = MyUiProps(
message = by remember { mutableStateOf("") }
)
props.message = "Yay no bolierplate"
CodePudding user response:
Just use MyUiProps
as the state you are exposing in ViewModel. Exposing single fields is tedious and unreadable.
If you have something like this in ViewModel:
// Expose whole state in ViewModel
private val _state = MutableStateFlow(MyUiProps()) // this will set default values in the model
val state: StateFlow<MyUiProps> = _state // observe this as state in compose
// inside ViewModel if you want to update the state
_state.value = _state.value.copy(fieldYouWantToUpdate = newValue)
// in composable
val state by viewModel.state.collectAsState()
CodePudding user response:
You cannot use by
inside a constructor. Still, you don't need the so-called "boilerplate" that you are showing. This is how you do it:
data class MyUiProps(
var message: MutableState<String>
) {
}
val props = MyUiProps(
message = remember { mutableStateOf("") }
)
props.message.value = "Yay no bolierplate"
You need to be using MutableState
and not just State
in your data classes parameter. This avoids the need to use observeAsState