Skip to content

Navigation

Starter Template uses navigation3 with Koin. All navigation logic lives inside the features/navigation module.


1. Define Screens

Example:

features/navigation/src/commonMain/.../screens/AuthScreens.kt
1
2
3
4
5
6
7
@Serializable
sealed class AuthScreens : NavKey {

    @Serializable
    data object SignIn : AuthScreens()

}

Rules

  • Must be @Serializable
  • Must extend NavKey * Use sealed class per feature

2. Register Screens for Serialization

Add the screen to the back stack configuration:

features/navigation/src/commonMain/.../StarterBackStack.kt
@Composable
fun rememberStarterBackStack(vararg initialScreens: NavKey): NavBackStack<NavKey> {
    return rememberNavBackStack(
        elements = initialScreens
    ) {
        ...
        // Register new screens
        subclass(AuthScreens.SignIn::class)
    }
}

State Restoration

If a screen is not registered using subclass(...), state restoration will fail.


3. Define Navigation Routes (Koin)

composeApp/src/commonMain/.../core/navigation/NavigationModule.kt
val navigationModule = module {

    navigation<AuthScreens.SignIn> {
        val navigator = StarterNavigator.getCurrent()

        SignInScreen(
            onSignedIn = {
                navigator.navigateTo(StarterScreens.Home)
            }
        )
    }
}

Custom Module

You can also create a custom module inside your feature di package, don't forget to include it inside initKoin


4. Navigating Between Screens

Get navigator:

val navigator = StarterNavigator.getCurrent()

Available methods:

navigator.navigateTo(route)
navigator.popAndNavigate(route)
navigator.popAllAndNavigate(route)
navigator.navigateOrBringToTop(route)
navigator.navigateUp()

Example:

navigator.popAllAndNavigate(StarterScreens.Home)

5. Changing Initial Screen

You can change the initial (starting) screen from App.kt.

By default, navigation starts from StarterScreens.Splash. To change it, update the first parameter of StarterNavigation.

composeApp/src/commonMain/kotlin/com/kmpstarter/App.kt
@Composable
private fun MainApp(
    ...
) {
    ...
    AppUpdateProvider(
        ...
    ) {
        LocaleProvider(
            overrideDefault = StarterLocales.ENGLISH
        ) {
            CompositionLocalProvider(...) {
                ApplicationTheme(
                    ...
                ) {
                    Scaffold(
                        ...
                    ) { _: PaddingValues ->
                        StarterNavigation(
                            StarterScreens.Splash, // Change this
                            modifier = Modifier
                        )
                    }
                }
            }
        }
    }
}

For example, to start directly from SignIn:

1
2
3
4
StarterNavigation(
    AuthScreens.SignIn,
    modifier = Modifier
)

Keeping Splash → Onboarding → Your Screen Flow

If you want to keep the default flow:

Splash → Onboarding → Your Screen

You can control it inside the navigation module.

composeApp/src/commonMain/.../navigation/NavigationModule.kt
val navigationModule = module {
    ...
    navigation<StarterScreens.Splash> { route ->
        ...
        SplashScreen(
            onNavigate = {
                navigator.popAndNavigate(
                    route = StarterScreens.Welcome
                )
            },
            ...
        )
    }
    navigation<StarterScreens.Onboarding> { route ->
        ...
        OnboardingV1Screen(
            onNavigate = {
                navigator.popAndNavigate(
                    route = StarterScreens.Welcome
                )
            }
        )
    }
    ...
}

Navigation Flow

  1. Create screen (extend NavKey)
  2. Register in StarterBackStack
  3. Add route in NavigationModule or custom module
  4. Use StarterNavigator to navigate
  5. Change inital screen if needed

Navigation is type-safe, serializable, and works across Compose Multiplatform.