Reputation: 2255
The Code A is from the Android official compose sample project.
1: I don't understand why author need to assign the varaiable currentScreen
for the two times in Code A, and it seems that both Code B and Code C can work well, could you tell me why?
2: In Code A onScreenChange = { screen -> currentScreen = RallyScreen.valueOf(screen) }
, where is the paramter screen
(It's a sting) assigned?
3: Is Code A a good design? It's hard to understand. If Code C is correct, I think the Code C is easy to see, how about my thinking?
Code A
@Composable
fun RallyApp() {
RallyTheme {
val allScreens = RallyScreen.values().toList()
var currentScreen by rememberSaveable { mutableStateOf(RallyScreen.Overview) }
Scaffold(
topBar = {
RallyTabRow(
allScreens = allScreens,
onTabSelected = { screen -> currentScreen = screen }, //It's One time
currentScreen = currentScreen
)
}
) { innerPadding ->
Box(Modifier.padding(innerPadding)) {
currentScreen.content(
onScreenChange = { screen ->
currentScreen = RallyScreen.valueOf(screen) //It's Two time
}
)
}
}
}
}
enum class RallyScreen(
val icon: ImageVector,
val body: @Composable ((String) -> Unit) -> Unit
) {
Overview(
icon = Icons.Filled.PieChart,
body = { OverviewBody() }
),
Accounts(
icon = Icons.Filled.AttachMoney,
body = { AccountsBody(UserData.accounts) }
),
Bills(
icon = Icons.Filled.MoneyOff,
body = { BillsBody(UserData.bills) }
);
@Composable
fun content(onScreenChange: (String) -> Unit) {
body(onScreenChange)
}
...
}
Code B
@Composable
fun RallyApp() {
RallyTheme {
...
) { innerPadding ->
Box(Modifier.padding(innerPadding)) {
currentScreen.content(
onScreenChange = {null } // I changed
)
}
}
}
}
...//The same as Code A
Code C
@Composable
fun RallyApp() {
RallyTheme {
...
) { innerPadding ->
Box(Modifier.padding(innerPadding)) {
currentScreen.body()
}
}
}
}
enum class RallyScreen(
val icon: ImageVector,
val body: @Composable () -> Unit //I changed
) {
Overview(
icon = Icons.Filled.PieChart,
body = { OverviewBody() }
),
Accounts(
icon = Icons.Filled.AttachMoney,
body = { AccountsBody(UserData.accounts) }
),
Bills(
icon = Icons.Filled.MoneyOff,
body = { BillsBody(UserData.bills) }
);
...
}
Added:
For the question 2, body = { BillsBody(UserData.bills) }
ignore the paramter ((String) -> Unit)
just like the following code.
Bills(
icon = Icons.Filled.MoneyOff,
body = { BillsBody(UserData.bills) } //It ignore the paramter ((String) -> Unit)
);
So in Code A onScreenChange = { screen -> currentScreen = RallyScreen.valueOf(screen) },
is not used because the main branch of the project is not complete code, right?
Upvotes: 0
Views: 104
Reputation: 10493
Answering your question 1, currentScreen
is updated two time because there are two ways to change the current screen. User can either click on one of the TabRow icons or use the buttons in Overview Screen to navigate to a different screen. And the onScreenChange
lambda in RallyScreen.content()
function is to handle the second case (where current screen can be changed from inside of a screen)
Code B and C are working because the argument ((String) -> Unit
) that RallyScreen.body
takes is not used anywhere. That's why you are able to remove it in Code C. In Code B you are just ignoring that callback. null
is not doing anything there.
Ideally this lambda argument should have been passed to the Overview screen. If you see the OverviewScreen composable, it accepts 3 parameters onClickSeeAllAccounts
, onClickSeeAllBills
and onAccountClick
. That lambda should have been used to provide values to these arguments. But the author chose to ignore these and just use the default value {}
for these params. That's why the "See All" buttons don't work in the starting point of codelab.
If you provide proper values to the three params, you will be able to navigate to Bills and Accounts screens from the Overview screen.
Upvotes: 1