Regarding Android Jetpack-Compose it is common practice to put your logic-parts inside your view-model. Thats why I am using UI-States to abstract my businesslogic from what I display. But where should I put necessary "visual-decision-logic" at.
This question is intended as a generic question of architecture best-practice in android compose.
visual-decision-logic := what is to be displayed depending on UI-State
/**
* this is the topmost Composable which is called by the Activity
*/
@Composable
fun DashboardScreen(dashboardViewModel: DashboardViewModel = hiltViewModel()) {
val dashboardLayoutState: DashboardLayoutState? by
dashboardViewModel.dashboardLayoutState.collectAsStateWithLifecycle()
val isLongpressHintDisplayed: Boolean by
dashboardViewModel.isLongpressHintDisplayed.collectAsStateWithLifecycle(
initialValue = false
)
DashboardComposable(
isLongpressHintDisplayed,
dashboardLayoutState
)
}
@Composable
private fun DashboardComposable(
isHintDisplayed: Boolean,
dashboardLayoutState: DashboardLayoutState?
) {
val pagerState = rememberPagerState(pageCount = { dashboardLayoutState.pages.size })
HorizontalPager(
state = pagerState
) { index ->
// question is about this if statement/logic
// also a longer when-statement could be here instead
if (isHintDisplayed) {
HintComposable()
} else {
DashboardPageComposable(
pageIndex = index
)
}
}
}
pro: straight forward coding, local-variables like pagerState are stored and used where needed con: nested components are not "good" testable because of "hidden" paths and may get worst in bigger compose-trees
It is perfectly fine to use if-else
in a Composable to discern which Composables to display depending on the state. The ViewModel should encapsulate business logic as
If you need some way to conditionally display a Composable, this can only be achieved using an if-else
in a Composable. But if you instead use if-else
in a Composable to modify state variables based on conditions, this would not be a good practice.
Have a look at the Jetpack Compose Samples Repository. It is an official repository maintained by Google and thus incorporating the best practices. You will find many if-else snippets within Composables when browsing the source code:
Conversations.kt
if (index == messages.size - 1) {
item {
DayHeader("20 Aug")
}
} else if (index == 2) {
item {
DayHeader("Today")
}
}
// ...
if (isFirstMessageByAuthor) {
// Last bubble before next author
Spacer(modifier = Modifier.height(8.dp))
} else {
// Between bubbles
Spacer(modifier = Modifier.height(4.dp))
}
ReplyAppBars.kt
if (searchResults.isNotEmpty()) {
LazyColumn(
modifier = Modifier.fillMaxWidth(),
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
// ...
}
} else if (query.isNotEmpty()) {
Text(
text = stringResource(id = R.string.no_item_found),
modifier = Modifier.padding(16.dp)
)
} else {
Text(
text = stringResource(id = R.string.no_search_history),
modifier = Modifier.padding(16.dp)
)
}
CraneHome.kt
When having more than three different branches, consider using the Kotlin when
construct for better readability:
when (craneScreenValues[page]) {
CraneScreen.Fly -> {
suggestedDestinations?.let { destinations ->
ExploreSection(
// ...
)
}
}
CraneScreen.Sleep -> {
ExploreSection(
// ...
)
}
CraneScreen.Eat -> {
ExploreSection(
// ...
)
}
}
Note that the conditions within the if and else if blocks are kept fairly simple. If you end up having very complex conditional expressions involving many different state variables from the ViewModel, then you might need to further encapsulate the state in the ViewModel by introducing a separate UIState
class.