Skip to content

Commit d8031d5

Browse files
author
Mike Lau
committed
Updated to use Navigation 3 and AGP 9
1 parent 514c51f commit d8031d5

27 files changed

Lines changed: 179 additions & 276 deletions

File tree

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,21 +42,22 @@ Each feature module is built with a sub-module that follows Domain, Data and UI
4242
> Details page use:
4343
> pokedex://pokemon/{id} where id is the pokemon number
4444
45+
* **Uses AGP 9!**
4546
* **Coil Image Loading:** Attempt to load an image using Coil with or without loading animation
4647
* **CI/CD Using Github:** Utilizing Github Actions and Github Releases page for pipelines and deployment (uses debug apk but you can build release or sign as you like)
4748
* **R8:** Release and R8 ready, just make sure to generate your own keystore / signing capability
4849
* **Lib Versions Catalog:** Uses new lib.versions.toml versions catalog to manage dependencies and their versions
4950

5051
## Built With
5152

52-
This project is built using next generation tools supported by the latest Canary version of **Android Studio Narwhal | 2025.1.1 Canary 7:**. Kotlin 2.1.20 with Compose Compiler Gradle Ready!
53+
This project is built using next generation tools supported by the latest Canary version of **Android Studio Panda 4 | 2025.3.4 Canary 1:**. Kotlin 2.3.20 with Compose Compiler Gradle Ready!
5354

54-
* **Android Studio Narwhal:** IDE used
55+
* **Android Studio Panda:** IDE used
5556
* **Kotlin:** Programming Language
5657
* **K2 Compiler:** Enabled
5758
* **KSP:** Uses KSP for hilt
5859
* **Jetpack Compose:** UI Toolkit
59-
* **Jetpack Libraries:** Lifecycle, View Model, Material 3, Navigation Compose etc.
60+
* **Jetpack Libraries:** Lifecycle, View Model, Material 3, Navigation 3 Compose etc.
6061
* **Hilt:** Dependency Injection
6162
* **Coil:** Image Loading
6263
* **Retrofit & OkHttp:** Networking and API management

app/build.gradle.kts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
plugins {
22
alias(libs.plugins.androidApplication)
3-
alias(libs.plugins.kotlinAndroid)
43
alias(libs.plugins.hilt)
54
alias(libs.plugins.kotlinSerialization)
65
alias(libs.plugins.kotlinKsp)
@@ -52,9 +51,6 @@ android {
5251
sourceCompatibility = JavaVersion.VERSION_17
5352
targetCompatibility = JavaVersion.VERSION_17
5453
}
55-
kotlinOptions {
56-
jvmTarget = "17"
57-
}
5854
kotlin {
5955
jvmToolchain(17)
6056
}
@@ -97,8 +93,11 @@ dependencies {
9793
implementation(libs.ui.tooling.preview)
9894
implementation(libs.material3)
9995
implementation(libs.material.icons.extended)
100-
implementation(libs.navigation.compose)
96+
implementation(libs.nav3.runtime)
97+
implementation(libs.nav3.ui)
98+
implementation(libs.lifecycle.nav3)
10199
implementation(libs.splashscreen)
100+
implementation(libs.hilt.navigation.compose)
102101
implementation(libs.hilt)
103102
ksp(libs.hilt.compiler)
104103
testImplementation(libs.junit)

app/src/main/java/com/mikelau/pokedex/MainActivity.kt

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,41 +13,34 @@ import androidx.compose.material3.Surface
1313
import androidx.compose.runtime.Composable
1414
import androidx.compose.ui.Modifier
1515
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
16-
import androidx.navigation.NavHostController
17-
import androidx.navigation.compose.rememberNavController
1816
import com.mikelau.core.common.utils.ColorBackground
1917
import com.mikelau.pokedex.navigation.AppNavGraph
20-
import com.mikelau.pokedex.navigation.NavigationProvider
18+
import com.mikelau.pokedex.navigation.rememberAppNavBackStack
2119
import dagger.hilt.android.AndroidEntryPoint
22-
import javax.inject.Inject
2320

2421
@AndroidEntryPoint
2522
class MainActivity : ComponentActivity() {
2623

27-
@Inject
28-
lateinit var navigationProvider: NavigationProvider
29-
3024
override fun onCreate(savedInstanceState: Bundle?) {
3125
installSplashScreen()
3226
super.onCreate(savedInstanceState)
33-
3427
enableEdgeToEdge()
3528
setContent {
36-
val navController = rememberNavController()
37-
App(navHostController = navController, navigationProvider)
29+
App()
3830
}
3931
}
4032
}
4133

4234
@Composable
43-
fun App(navHostController: NavHostController, navigationProvider: NavigationProvider) {
35+
fun App() {
4436
Surface(
4537
modifier = Modifier
4638
.background(ColorBackground)
4739
.fillMaxSize()
4840
.windowInsetsPadding(WindowInsets.systemBars),
4941
color = ColorBackground
5042
) {
51-
AppNavGraph(navController = navHostController, navigationProvider = navigationProvider)
43+
val backStack = rememberAppNavBackStack()
44+
AppNavGraph(backStack = backStack)
5245
}
5346
}
Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
@file:Suppress("unused")
2+
13
package com.mikelau.pokedex.di
24

35
import com.mikelau.feature.pokemon.ui.navigation.PokemonApi
6+
import com.mikelau.feature.pokemon.ui.navigation.PokemonApiImpl
47
import com.mikelau.feature.pokemondetails.ui.navigation.PokemonDetailsApi
5-
import com.mikelau.pokedex.navigation.NavigationProvider
8+
import com.mikelau.feature.pokemondetails.ui.navigation.PokemonDetailsApiImpl
69
import dagger.Module
710
import dagger.Provides
811
import dagger.hilt.InstallIn
@@ -13,9 +16,9 @@ import dagger.hilt.components.SingletonComponent
1316
object AppModule {
1417

1518
@Provides
16-
fun provideNavigationProvider(pokemonApi: PokemonApi, pokemonDetailsApi: PokemonDetailsApi): NavigationProvider {
17-
return NavigationProvider(pokemonApi, pokemonDetailsApi)
18-
}
19+
fun providePokemonApi(): PokemonApi = PokemonApiImpl()
1920

21+
@Provides
22+
fun providePokemonDetailsApi(): PokemonDetailsApi = PokemonDetailsApiImpl()
2023
}
2124

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,48 @@
1+
@file:Suppress("DEPRECATION", "OPT_IN_USAGE")
2+
13
package com.mikelau.pokedex.navigation
24

35
import androidx.compose.runtime.Composable
4-
import androidx.navigation.NavHostController
5-
import androidx.navigation.compose.NavHost
6-
import com.mikelau.core.common.utils.PokemonFeature
6+
import androidx.hilt.navigation.compose.hiltViewModel
7+
import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator
8+
import androidx.navigation3.runtime.NavBackStack
9+
import androidx.navigation3.runtime.NavEntry
10+
import androidx.navigation3.runtime.NavKey
11+
import androidx.navigation3.runtime.rememberNavBackStack
12+
import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator
13+
import androidx.navigation3.ui.NavDisplay
14+
import com.mikelau.core.AppNavDestination
15+
import com.mikelau.feature.pokemon.ui.screen.PokemonScreen
16+
import com.mikelau.feature.pokemon.ui.screen.PokemonViewModel
17+
import com.mikelau.feature.pokemondetails.ui.screen.PokemonDetailsScreen
18+
import com.mikelau.feature.pokemondetails.ui.screen.PokemonDetailsViewModel
719

820
@Composable
9-
fun AppNavGraph(
10-
navController: NavHostController,
11-
navigationProvider: NavigationProvider
12-
) {
13-
14-
NavHost(navController = navController, startDestination = PokemonFeature.nestedRoute){
15-
navigationProvider.pokemonApi.registerGraph(
16-
navController,this
17-
)
21+
fun rememberAppNavBackStack(): NavBackStack<NavKey> =
22+
rememberNavBackStack(AppNavDestination.PokemonList)
1823

19-
navigationProvider.pokemonDetailsApi.registerGraph(
20-
navController,this
21-
)
22-
}
23-
}
24+
@Suppress("UNCHECKED_CAST")
25+
@Composable
26+
fun AppNavGraph(backStack: NavBackStack<NavKey>) {
27+
val typedBackStack = backStack as NavBackStack<AppNavDestination>
28+
NavDisplay(
29+
backStack = typedBackStack,
30+
entryDecorators = listOf(
31+
rememberSaveableStateHolderNavEntryDecorator(),
32+
rememberViewModelStoreNavEntryDecorator(),
33+
),
34+
onBack = { typedBackStack.removeLastOrNull() },
35+
entryProvider = { key ->
36+
when (key) {
37+
is AppNavDestination.PokemonList -> NavEntry(key) {
38+
val viewModel = hiltViewModel<PokemonViewModel>()
39+
PokemonScreen(viewModel = viewModel, backStack = typedBackStack)
40+
}
41+
is AppNavDestination.PokemonDetails -> NavEntry(key) {
42+
val viewModel = hiltViewModel<PokemonDetailsViewModel>()
43+
PokemonDetailsScreen(id = key.id, viewModel = viewModel, backStack = typedBackStack)
44+
}
45+
}
46+
}
47+
)
48+
}

app/src/main/java/com/mikelau/pokedex/navigation/NavigationProvider.kt

Lines changed: 0 additions & 9 deletions
This file was deleted.

core/common/build.gradle.kts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
plugins {
22
alias(libs.plugins.androidLibrary)
3-
alias(libs.plugins.kotlinAndroid)
43
}
54

65
android {
@@ -27,9 +26,6 @@ android {
2726
sourceCompatibility = JavaVersion.VERSION_17
2827
targetCompatibility = JavaVersion.VERSION_17
2928
}
30-
kotlinOptions {
31-
jvmTarget = "17"
32-
}
3329
kotlin {
3430
jvmToolchain(17)
3531
}

core/feature/build.gradle.kts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
plugins {
22
alias(libs.plugins.androidLibrary)
3-
alias(libs.plugins.kotlinAndroid)
3+
alias(libs.plugins.kotlinSerialization)
4+
alias(libs.plugins.compose.compiler)
45
}
56

67
android {
@@ -27,9 +28,6 @@ android {
2728
sourceCompatibility = JavaVersion.VERSION_17
2829
targetCompatibility = JavaVersion.VERSION_17
2930
}
30-
kotlinOptions {
31-
jvmTarget = "17"
32-
}
3331
kotlin {
3432
jvmToolchain(17)
3533
}
@@ -39,7 +37,10 @@ dependencies {
3937
implementation(libs.core.ktx)
4038
implementation(libs.appcompat)
4139
implementation(libs.material)
42-
implementation(libs.navigation.compose)
40+
implementation(libs.kotlinx.serialization.json)
41+
implementation(libs.nav3.runtime)
42+
implementation(libs.nav3.ui)
43+
implementation(libs.lifecycle.nav3)
4344
testImplementation(libs.junit)
4445
androidTestImplementation(libs.androidx.test.ext.junit)
4546
androidTestImplementation(libs.espresso.core)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.mikelau.core
2+
3+
import androidx.navigation3.runtime.NavKey
4+
import kotlinx.serialization.Serializable
5+
6+
sealed interface AppNavDestination : NavKey {
7+
@Serializable
8+
data object PokemonList : AppNavDestination
9+
10+
@Serializable
11+
data class PokemonDetails(val id: String) : AppNavDestination
12+
}
Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
package com.mikelau.core
22

3-
import androidx.navigation.NavGraphBuilder
4-
import androidx.navigation.NavHostController
5-
6-
interface FeatureApi {
7-
8-
fun registerGraph(
9-
navController: NavHostController,
10-
navGraphBuilder: NavGraphBuilder
11-
)
3+
import androidx.navigation3.runtime.NavBackStack
4+
import androidx.navigation3.runtime.NavKey
125

6+
fun interface FeatureApi {
7+
fun registerGraph(backStack: NavBackStack<NavKey>)
138
}

0 commit comments

Comments
 (0)