https://kotlinlang.org logo
Title
t

Tom De Decker

04/27/2023, 10:26 AM
Does anyone have an opinion on whether it's considered a bad practice to make the ViewModel aware of the screen configuration/orientation? I have a case where I'd like to hide some of the page's content in a tab of a tab bar for phones/portrait mode devices while it can be shown on larger devices (landscape/tablets). A list of what tabs to show currently comes from a ViewModel, but the ViewModel is not aware of the window configuration. In essence, it boils down to something like this:
val tabs = if (configuration.isExpanded) {
    // Tablet/landscape mode
    listOf(
        TabType.Foo,
        TabType.Bar,
    )
} else {
    // Phone/portrait mode
    listOf(
        TabType.Foo,
        TabType.Bar,
        TabType.MoreInfo,
    )
}
Is this something I'd better avoid? If so, what would a better pattern to handle this be? In practice, the list of tabs is a StateFlow which is collected by a composable, but conceptionally this shouldn't really make a difference.
o

Otávio Gabriel (Tavieto)

04/27/2023, 9:37 PM
In my opinion, the UI should have as little knowledge as possible about the data manipulation. Your approach is valid and nice, but I imagine that can cause some visual bug. For example, your app is opened in landscape and you switch to another app. This other app now is in portrait. When you come back to your app, your view model will have the information that the screen still in landscape and depends of the screen to inform about the new orientation. So is possible to user see the UI change, which could not be a good experience. You could have 2 data list in your view model about each orientation and leave to UI decide which one to choose. I would choose this approach:
val config = LocalConfiguration.current
val myList = when (config.orientation) {
    androidx.compose.foundation.gestures.Orientation.Vertical.ordinal -> viewModel.listPortrait
    else -> viewModel.listLandscape
}
t

Tom De Decker

04/28/2023, 8:53 AM
Thanks for the reply, that makes sense! What makes this difficult, however, is that I currently store the active tab and its index inside the ViewModel. Without information about the orientation, the ViewModel can't really "know" what the active tab is or what the bounds are on the list of tabs (e.g. listPortrait might contain 3 items while listLandscape contains only 2). I feel like this makes it quite difficult to properly use a ViewModel.
o

Otávio Gabriel (Tavieto)

04/28/2023, 2:52 PM
You can also keep the index list in screen and use the same principle. I recommend use rememberSaveable to keep the data across screen orientation and recreation. This way the screen will be responsible for handle your lists.
var indexPortrait by rememberSaveable { mutableStateOf(0) }
var indexLandscape by rememberSaveable { mutableStateOf(0) }
val index by remember(indexPortrait, indexLanscape) { 
    mutableStateOf(
        when (config.orientation) {
    androidx.compose.foundation.gestures.Orientation.Vertical.ordinal -> indexPortrait
    else -> indexLandscape
}
    ) 
}
Here is a reference about rememberSaveable: https://developer.android.com/jetpack/compose/state-saving
c

Colin T

04/30/2023, 10:27 PM
var myOrientation by remember{ mutableStateOf(Configuration.ORIENTATION_PORTRAIT) }
LaunchedEffect(configuration) {
    // Save any changes to the orientation value on the configuration object
    snapshotFlow { configuration.orientation }
        .collectLatest { myOrientation = it }
}
when(myOrientation){
    Configuration.ORIENTATION_LANDSCAPE -> {
        YourComposable(navController)
    }
    else -> { ......