Is someone aware of a sample app where data is not...
# compose
l
Is someone aware of a sample app where data is not only retrieved but also send to repository? I'm wondering how to collect all the state in different composable when event is triggered. First thought is to hoist all the state to
ViewModel
...
b
I typically do something like that. I'll write a "Screen" Composable that takes a viewModel, and observes a composite state value from it. Then pass pieces of the composite state down to stateless child composables that make up the different parts of the screen. Something kind of like this:
Copy code
data class HomeScreenState(
    val loading: Boolean,
    val dataValue: DataModel?,
    val moreData: MoreModel?
)
Copy code
class MyViewModel(myRepo: MyRepository): ViewModel {
    var homeScreenState by mutableStateOf(HomeScreenState(loading = true, dataValue = null, moreData = null)) 
        private set

    init {
        viewModelScope.launch { 
            // This is contrived, but you get the idea
            val newData = myRepo.loadSomeData()
            homeScreenState = homeScreenState.copy(dataValue = newData)
            val moreData = myRepo.loadMoreData()
            homeScreenState = homeScreenState.copy(moreData = moreData)   
            homeScreenState = homeScreenState.copy(loading = false)
        }
    }

    fun onSomeEvent() {
        viewModelScope.launch {
            homeScreenState = homeScreenState.copy(loading = true)
            val newData = myRepo.fetchNewData()
            homeScreenState = homeScreenState.copy(loading = false, dataValue = newData) 
        }
    }
}
Copy code
@Composable
fun HomeScreen(viewModel: MyViewModel) {
    val (loading, data, moreData) = viewModel.homeScreenState

    if (loading) {
       CircularProgressIndicator()
    } else {
       SomeComposable(visibleData = data, onSomeEvent = { viewModel.onSomeEvent() })
       OtherComposable(moreData)
    }
}
s
Do you need remember on val (loading, data, moreData) = viewModel.homeScreenState ?
m
@Shakil Karim the viewmodel outlives a single composition so you don't need to remember the state to retain its identity across the compositions
s
@manueldidonna Ah, I see. Do you use a separate ViewModel for each Component Screen?
l
Thanks guys. I'm not sking how to fetch data and let UI consume it. I'm asking for the opposite way...read the current state of controls (e.g. selected checkboxes, sliders, textfields etc.), encapsulate it and send it back to the data source to update the source.
b
@Lilly - Ah. The short answer to your question is yes, I would typically use event handler lambda's to get update values from Textfields, Checkboxes, etc and pass them back up the compose tree and to my ViewModel, and update state there in the viewmodel. The above example still applies. Notice in the
HomeScreen
composable where it calls
SomeComposable
... There, it's passing it a lambda for
onSomeEvent
...
SomeComposable
very well could hook that lambda up to a TextField's
onValueChanged
event handler (of course, it would have to be modified a little bit to take a string argument with the new text value), which then would have the effect of calling the viewModel's
onSomeEvent
method, where you could store the text (either in memory in the viewmodel, or in a database, or send it off to a network api, etc), and update some
State
value that your composables are observing so they can react/recompose with the new value.
👍 2
l
@Bradleycorn Thanks for your input. Thats a legit solution. An alternative would be to define the
mutableStateOf
directly in the viewModel and pass the state to their respective composables. What do you think about that? Like:
Copy code
SomeComposable(vieModel.state)
I will have ~80 of these composables
b
Yep. That's typically what I do:
Copy code
class MyViewModel: ViewModel() {
    var state by mutableStateOf(someValue)
        private set

    fun onSomeEvent() {
         state = newValue
    }
}
Copy code
@Composable
fun MyScreen(viewModel: MyViewModel) {
   MyChildComposable(viewModel.state)
   AnotherChildComposable(viewModel.state, onClick = { viewModel.onSomeEvent() }) 
}
The State Codelab (https://developer.android.com/codelabs/jetpack-compose-state) that @Sean McQuillan [G] wrote details this pattern and the hows and whys of each piece.
l
Thanks a lot! 🙏