Skip to content

Commit 92a9de0

Browse files
authored
Merge pull request #73 from android/nav3
Split app to 2 screens with Navigation3 and NavigationStack
2 parents b567e48 + 54efca6 commit 92a9de0

File tree

15 files changed

+393
-169
lines changed

15 files changed

+393
-169
lines changed

Fruitties/androidApp/build.gradle.kts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,16 @@ plugins {
1717
alias(libs.plugins.androidApplication)
1818
alias(libs.plugins.kotlinAndroid)
1919
alias(libs.plugins.compose.compiler)
20+
alias(libs.plugins.kotlinxSerialization)
2021
}
2122

2223
android {
2324
namespace = "com.example.fruitties.android"
24-
compileSdk = 35
25+
compileSdk = 36
2526
defaultConfig {
2627
applicationId = "com.example.fruitties.android"
2728
minSdk = 26
28-
targetSdk = 35
29+
targetSdk = 36
2930
versionCode = 1
3031
versionName = "1.0"
3132
}
@@ -79,4 +80,8 @@ dependencies {
7980
implementation(libs.androidx.paging.compose.android)
8081
implementation(libs.androidx.lifecycle.viewmodel.compose)
8182
debugImplementation(libs.compose.ui.tooling)
83+
implementation(libs.androidx.navigation3.runtime)
84+
implementation(libs.androidx.navigation3.ui)
85+
implementation(libs.androidx.lifecycle.viewmodel.navigation3)
86+
implementation(libs.kotlinx.serialization.core)
8287
}

Fruitties/androidApp/src/main/java/com/example/fruitties/android/MainActivity.kt

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,25 @@ import androidx.activity.enableEdgeToEdge
2323
import androidx.compose.foundation.layout.fillMaxSize
2424
import androidx.compose.material3.MaterialTheme
2525
import androidx.compose.material3.Surface
26+
import androidx.compose.runtime.Composable
2627
import androidx.compose.ui.Modifier
28+
import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator
29+
import androidx.navigation3.runtime.NavKey
30+
import androidx.navigation3.runtime.entry
31+
import androidx.navigation3.runtime.entryProvider
32+
import androidx.navigation3.runtime.rememberNavBackStack
33+
import androidx.navigation3.runtime.rememberSavedStateNavEntryDecorator
34+
import androidx.navigation3.ui.NavDisplay
35+
import androidx.navigation3.ui.rememberSceneSetupNavEntryDecorator
36+
import com.example.fruitties.android.ui.CartScreen
2737
import com.example.fruitties.android.ui.ListScreen
38+
import kotlinx.serialization.Serializable
39+
40+
@Serializable
41+
data object ListScreenKey : NavKey
42+
43+
@Serializable
44+
data object CartScreenKey : NavKey
2845

2946
class MainActivity : ComponentActivity() {
3047
override fun onCreate(savedInstanceState: Bundle?) {
@@ -36,9 +53,40 @@ class MainActivity : ComponentActivity() {
3653
modifier = Modifier.fillMaxSize(),
3754
color = MaterialTheme.colorScheme.background,
3855
) {
39-
ListScreen()
56+
NavApp()
4057
}
4158
}
4259
}
4360
}
4461
}
62+
63+
@Composable
64+
fun NavApp() {
65+
val backStack = rememberNavBackStack(ListScreenKey)
66+
67+
NavDisplay(
68+
backStack = backStack,
69+
entryDecorators = listOf(
70+
rememberSceneSetupNavEntryDecorator(),
71+
rememberSavedStateNavEntryDecorator(),
72+
rememberViewModelStoreNavEntryDecorator(),
73+
),
74+
onBack = { keysToRemove -> repeat(keysToRemove) { backStack.removeLastOrNull() } },
75+
entryProvider = entryProvider {
76+
entry<ListScreenKey> {
77+
ListScreen(
78+
onClickViewCart = {
79+
backStack.add(CartScreenKey)
80+
},
81+
)
82+
}
83+
entry<CartScreenKey> {
84+
CartScreen(
85+
onNavBarBack = {
86+
backStack.removeIf { it is CartScreenKey }
87+
},
88+
)
89+
}
90+
},
91+
)
92+
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/*
2+
* Copyright 2024 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.fruitties.android.ui
18+
19+
import androidx.compose.foundation.layout.Column
20+
import androidx.compose.foundation.layout.Spacer
21+
import androidx.compose.foundation.layout.WindowInsets
22+
import androidx.compose.foundation.layout.WindowInsetsSides
23+
import androidx.compose.foundation.layout.fillMaxWidth
24+
import androidx.compose.foundation.layout.only
25+
import androidx.compose.foundation.layout.padding
26+
import androidx.compose.foundation.layout.safeDrawing
27+
import androidx.compose.foundation.layout.systemBars
28+
import androidx.compose.foundation.layout.windowInsetsBottomHeight
29+
import androidx.compose.foundation.lazy.LazyColumn
30+
import androidx.compose.foundation.lazy.items
31+
import androidx.compose.material.icons.Icons
32+
import androidx.compose.material.icons.automirrored.filled.ArrowBack
33+
import androidx.compose.material3.CenterAlignedTopAppBar
34+
import androidx.compose.material3.ExperimentalMaterial3Api
35+
import androidx.compose.material3.Icon
36+
import androidx.compose.material3.IconButton
37+
import androidx.compose.material3.MaterialTheme
38+
import androidx.compose.material3.Scaffold
39+
import androidx.compose.material3.Text
40+
import androidx.compose.material3.TopAppBarDefaults
41+
import androidx.compose.runtime.Composable
42+
import androidx.compose.runtime.collectAsState
43+
import androidx.compose.runtime.getValue
44+
import androidx.compose.runtime.remember
45+
import androidx.compose.ui.Alignment
46+
import androidx.compose.ui.Modifier
47+
import androidx.compose.ui.platform.LocalContext
48+
import androidx.compose.ui.res.stringResource
49+
import androidx.compose.ui.unit.dp
50+
import androidx.lifecycle.viewmodel.compose.viewModel
51+
import com.example.fruitties.android.R
52+
import com.example.fruitties.android.di.App
53+
import com.example.fruitties.viewmodel.CartViewModel
54+
55+
@OptIn(ExperimentalMaterial3Api::class)
56+
@Composable
57+
fun CartScreen(onNavBarBack: () -> Unit) {
58+
// Instantiate a ViewModel with a dependency on the AppContainer.
59+
// To make ViewModel compatible with KMP, the ViewModel factory must
60+
// create an instance without referencing the Android Application.
61+
// Here we put the KMP-compatible AppContainer into the extras
62+
// so it can be passed to the ViewModel factory.
63+
val app = LocalContext.current.applicationContext as App
64+
val extras = remember(app) {
65+
val container = app.container
66+
CartViewModel.creationExtras(container)
67+
}
68+
val viewModel: CartViewModel = viewModel(
69+
factory = CartViewModel.Factory,
70+
extras = extras,
71+
)
72+
73+
val cartState by viewModel.cartUiState.collectAsState()
74+
75+
Scaffold(
76+
topBar = {
77+
CenterAlignedTopAppBar(
78+
navigationIcon = {
79+
IconButton(onClick = onNavBarBack) {
80+
Icon(
81+
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
82+
contentDescription = "Navigate back",
83+
)
84+
}
85+
},
86+
title = {
87+
Text(text = stringResource(R.string.frutties))
88+
},
89+
colors = TopAppBarDefaults.topAppBarColors(
90+
containerColor = MaterialTheme.colorScheme.primary,
91+
scrolledContainerColor = MaterialTheme.colorScheme.primary,
92+
navigationIconContentColor = MaterialTheme.colorScheme.onPrimary,
93+
titleContentColor = MaterialTheme.colorScheme.onPrimary,
94+
actionIconContentColor = MaterialTheme.colorScheme.onPrimary,
95+
),
96+
)
97+
},
98+
contentWindowInsets = WindowInsets.safeDrawing.only(
99+
// Do not include Bottom so scrolled content is drawn below system bars.
100+
// Include Horizontal because some devices have camera cutouts on the side.
101+
WindowInsetsSides.Top + WindowInsetsSides.Horizontal,
102+
),
103+
) { paddingValues ->
104+
Column(
105+
modifier = Modifier
106+
// Support edge-to-edge (required on Android 15)
107+
// https://developer.android.com/develop/ui/compose/layouts/insets#inset-size
108+
.padding(paddingValues)
109+
.padding(16.dp),
110+
) {
111+
val cartItemCount = cartState.totalItemCount
112+
Text(
113+
text = "Cart has $cartItemCount items",
114+
modifier = Modifier.padding(8.dp),
115+
)
116+
LazyColumn(
117+
modifier = Modifier.fillMaxWidth(),
118+
horizontalAlignment = Alignment.CenterHorizontally,
119+
) {
120+
items(cartState.cartDetails) { cartItem ->
121+
Text(text = "${cartItem.fruittie.name}: ${cartItem.count}")
122+
}
123+
item {
124+
Spacer(
125+
Modifier.windowInsetsBottomHeight(
126+
WindowInsets.systemBars,
127+
),
128+
)
129+
}
130+
}
131+
}
132+
}
133+
}

0 commit comments

Comments
 (0)