i have a best pracitice question: when having a st...
# compose
m
i have a best pracitice question: when having a state that could be null (eg when observing a viewMode, in a composable) using the
by
delegate, how do you deal with the case that the value is null? An
if
check is an option but the value can not be smart-cast. What are you doing in that case? Example:
Copy code
val user by viewModel.user.collectAsState(null)

if (user != null) {
  // user? can not be cast to user?
  // Message is "Smart cast to 'User' is impossible, because 'user' is a property that has open or custom getter."
}
For one state object you could also use
let
but that is very cumbersome when multiple values should be consumed. Using
!!
is also not so nice. Any tips?
plus one 1
c
We usually pass these state-s as parameters immediately after creating them from the ViewModel states. If you pass it as a Nullable parameter to another Composable, smart cast will work.
Copy code
@Composable
fun Screen() {
  val user by viewModel.user.collectAsState(null)

    ScreenContent(user)
  }
}

@Composable
private fun ScreenContent(user: User?) {
  if (user != null) {
    // user here should be non-null
  }
}
m
That is a valid approach indeed.
I found that if you only need to read the state you can also just use the state.value immediatly. eg
Copy code
val user = viewModel.user.collectAsState(null).value
Thereby you create the intermediate variable under the hood which can be smart-cast as expected.
m
Another solution, depending on the use case, you can keep the ScreenContent with non-null values, and only entering the screen when the state is not null - and let some loading screen until there. A way to do so it to handle this at the navigation level, listening to a channel from the view model, which will send an event to start a redirect when the state is ready
g
Late to the party, but on top of @Moritz Post’s approach I created an extension function and it reads nicely
Copy code
@Composable
fun <T> StateFlow<T>.collectAsStateValue(
): T = collectAsState().value

val user = viewModel.user.collectAsStateValue()
m
@goku Sounds good but the method should rather be
collectValue()
as it is not tied to a state anymore.