I’m looking for some architecture advices regardin...
# android
j
I’m looking for some architecture advices regarding view states, what do you think is the better option between : 1/ Creating a data class containing all the info a view need, and expose it through a
StateFlow
object from the view model. The view can then simply
collect
the updates and do whatever it needs. Something like this :
Copy code
data class ViewState {
    val isLoading: Boolean,
    @StringRes val error: Int,
    val items: List<Any>
}

class SomeViewModel : ViewModel() {
    private val _state = MutableStateFlow<ViewState>()
    val state: StateFlow<ViewState> = _state

    // All the logic to update _state
}  

class SomeFragment : Fragment() {

    ...
    
    override fun onViewCreated(...) {
        lifeCycleScope.launch {
            viewModel.state.collect { state ->
                // update the view
            }
        }
    }
}
2/ Instead of using a
StateFlow
and a data class containing all the data, use a sealed class to make “differential updates”
Copy code
sealed class ViewState {
    data class IsLoading(val isLoading: Boolean)
    data class Error (@StringRes val error: Int)
    data class ItemsLoaded(val items: List<Any>)
}

class SomeViewModel : ViewModel() {
    private val channel = Channel<ViewState>()
    val state: Flow<ViewState> = channel.receiveAsFlow()

    // All the logic to offer/emit new value
}  

class SomeFragment : Fragment() {

    ...
    
    override fun onViewCreated(...) {
        lifeCycleScope.launch {
            viewModel.state.collect { state ->
                // update the view
            }
        }
    }
}
what are the +/- to use one or the other solution? I feel the second option let me have a better overall control on how to update the view since I can react to specific events.
a
I feel the second option let me have a better overall control on how to update the view since I can react to specific events.
Really? I feel the other way around. How will you show an error without hiding the items? 🙂
Or show a loading indicator (say on refresh) on an already loaded items
f
definitely 1. there is an article that shows one disadvantage of
sealed class
as view state. of course it can work with `sealed class`es, but I think its easier, more concise and more deterministic with a
data class
. If a loading->error/success state is needed, that just have the
sealed class
as a
val
in your state
data class
j
well, the state flow doesn’t really tells me why there has been an update. So I receive a whole new objects and I have to go through some if/else conditions to decide what should be done. On the other hand, I can use a when statement to check out what subclass from the sealed class I receive, for example
state is Error
and then simply do whatever I need to do to show an error
I could add a
updateType
the the general data class to obtain sort of the same possibility 🤔
a
the state flow doesn’t really tells me why there has been an update.
because state flow is for state and not events or updates. if/else is just vanilla
when
if you have an Error State sealed you would only be able to render it’s properties. you don’t need updateType as well, you can just render an error if it’s available.
Copy code
data class State(
  val items: List<Item> = emptyList()
  val error: Error?
)

@Composable fun Screen(state: State) {
  
  error?.{ Text(message = it) }
}
So
State
class is not about updating some state, It is the state, that you render to view.
j
I remember having issues with updating correctly the ui when different use cases where producing the same apparent state. But it might just be me doing something wrong, I’ll give it another try
a
Hmm, shouldn’t be a problem with unidirectional data flow. Treat that State as the single source of truth, and keep it immutable. Then you won’t have a problem, I think. Anyway this is just one approach (Redux-like), you can check out other approaches in the compose-samples, they handle state in different ways.
a
even though this article is meant to explain state in compose, the principles of using state rather than events for UI updates apply if you're using views too: https://developer.android.com/jetpack/compose/state
by definition, the state in arrangements like this is everything relevant to show in the UI. If you need to know a "why" somewhere, that cause data should also be part of the state
j
Thanks for all the inputs/answers! Much appreciated 🙂
g
I am using the single data class approach with all the properties needed and then doing multiple state().map { specificProperty }.distinctUntilChanged().collect
j
oh that’s a nice trick I didn’t know about, thanks!
It’s been a while since the last message in this thread but I happen to be reading this page of the documentation : https://developer.android.com/jetpack/compose/architecture#example and they use a sealed class for the state. @Adam Powell did Google change its recommendations regarding this topic and what you said last time, or is it just by convenience for this particular example? I did find the opposite here : https://developer.android.com/topic/libraries/architecture/viewmodel.