Kompass
Compose Multiplatform Navigation
Install / Use
/learn @3xcool/KompassREADME
Kompass (KMP Navigation)
<p align="center"> <strong>Modern navigation library for Compose Multiplatform (Android, iOS and Desktop).</strong><br/> Designed to make navigation predictable, scalable, and platform-agnostic. Built around pure reducers and reactive state updates, it replaces traditional navigation patterns with a more composable and testable approach. </p> <p align="center"> <img src="assets/KompassOverview.png" alt="Kompass Overview" /> </p>About
Kompass is the next-generation navigation library designed from the ground up for Jetpack Compose. Unlike traditional navigation approaches, Kompass embraces functional programming principles and reactive architecture patterns:
- Pure State Management - Navigation state is immutable, serializable, and completely decoupled from UI logic. The entire navigation system is built on predictable, deterministic state transitions.
- Reducer Pattern - All navigation rules are side-effect free, making the navigation core trivial to test without mocking frameworks or instrumentation.
- Multi-Graph Architecture - Organize large applications across multiple modular navigation graphs with independent layouts and transitions.
- Lifecycle-Aware Scopes - Built-in scope management provides ViewModel-like instance storage with automatic cleanup and memory leak prevention.
- Deep Linking Made Simple - Extensible deep link handlers convert URIs into navigation commands with type-safe argument parsing.
- Result - Deliver typed results between destinations without tight coupling or callback hell.
- Persistent State - Automatic serialization and restoration across configuration changes, process death, and app relaunches.
- Customizable Layouts & Transitions - Per-graph scene layouts support any composition pattern: single-stack, master-detail, split-screen, or custom multi-pane designs.
Perfect for applications that need robust, scalable, and testable navigation without the complexity of over-engineered frameworks.
Contents
- Key Features
- Architecture
- Installation
- Quick Start
- Navigation Commands
- Navigation Scopes
- Navigation Results
- Custom Layouts & Transitions
- Deep Linking
- State Serialization
- Testing
- Configuration & Customization
- Performance Considerations
- Thread Safety
- Contributing
- Resources
Key Features
- Pure State Management - Navigation state is immutable and serializable, separate from UI logic
- Testable Reducer Pattern - All navigation rules are deterministic and side-effect free
- Pluggable Layouts & Transitions - Customize screen transitions and multi-pane layouts per graph
- Multi-Graph Support - Organize destinations across multiple navigation graphs
- Navigation Scopes - Lifecycle-aware instance management (ViewModel-like) with automatic cleanup
- Deep Linking - Built-in deep link support with extensible handlers
- Result Passing - Deliver navigation results between destinations
- Persistent State - Automatic serialization and restoration across configuration changes
Architecture
Kompass follows a clean separation of concerns:
Navigation Logic
↓
NavigationHandler (pure reducer: State + Command → State)
↓
NavigationState (immutable back stack)
↓
NavController (facade & effects)
↓
KompassNavigationHost (renders via NavigationGraph)
↓
Screen Content
Core Components
- NavigationState - Immutable representation of the back stack
- NavigationHandler - Pure reducer applying navigation commands
- NavController - Public API for performing navigation
- KompassNavigationHost - Root composable orchestrating rendering
- NavigationGraph - Maps destinations to UI content
- NavigationScopes - Thread-safe lifecycle-aware instance storage
- BackStackEntry - Represents a single stack entry with destination, args, and scope
Installation
Add to your build.gradle.kts:
repositories {
mavenCentral()
}
dependencies {
implementation("com.tekmoon:kompass:1.0.0")
implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
}
Quick Start
1. Define Destinations
sealed interface MainDestination : Destination {
data object Home : MainDestination {
override val id: String = "home"
}
data object Profile : MainDestination {
override val id: String = "profile"
}
data object Settings : MainDestination {
override val id: String = "settings"
}
}
2. Create a Navigation Graph
class MainNavigationGraph : NavigationGraph {
override fun canResolveDestination(destinationId: String): Boolean =
destinationId in setOf("home", "profile", "settings")
override fun resolveDestination(
destinationId: String,
args: String?
): Destination = when (destinationId) {
"home" -> MainDestination.Home
"profile" -> MainDestination.Profile
"settings" -> MainDestination.Settings
else -> error("Unknown destination: $destinationId")
}
@Composable
override fun Content(
entry: BackStackEntry,
destination: Destination,
navController: NavController
) {
when (destination) {
is MainDestination.Home -> HomeScreen(navController)
is MainDestination.Profile -> ProfileScreen(navController)
is MainDestination.Settings -> SettingsScreen(navController)
}
}
}
3. Setup Navigation Host
@Composable
fun AppNavigation() {
val navController = rememberNavController(
startDestination = MainDestination.Home
)
KompassNavigationHost(
navController = navController,
graphs = persistentListOf(MainNavigationGraph())
)
}
4. Navigate from Screens
@Composable
fun HomeScreen(navController: NavController) {
Button(
onClick = {
navController.navigate(
entry = BackStackEntry(
destinationId = "profile",
scopeId = newScope()
)
)
}
) {
Text("Go to Profile")
}
}
Navigation Commands
Navigate
Push a new destination onto the back stack:
navController.navigate(
entry = BackStackEntry(
destinationId = "profile",
args = """{"userId":"123"}""",
scopeId = newScope()
),
clearBackStack = false, // Clear entire stack
popUpTo = "home", // Pop up to destination
popUpToInclusive = false, // Include the destination in pop
reuseIfExists = false // Reuse existing entry
)
Pop
Remove one or more entries from the back stack:
// Pop single entry
navController.pop()
// Pop with result
navController.pop(result = ProfileResult(userId = "123"))
// Pop multiple entries
navController.pop(count = 2)
// Pop until destination
navController.pop(popUntil = "home")
Replace Root
Replace the entire back stack with a single entry:
navController.replaceRoot(
entry = BackStackEntry(
destinationId = "home",
scopeId = newScope()
)
)
Navigation Scopes
Navigation Scopes provide lifecycle-aware instance storage similar to ViewModels:
@Composable
fun ProfileScreen(navController: NavController, entry: BackStackEntry) {
val viewModel = rememberScoped<ProfileViewModel>(
scopeId = entry.scopeId,
factory = { ProfileViewModel() },
onCleared = { it.close() }
)
// ViewModel survives recomposition but is cleared when entry is popped
LaunchedEffect(Unit) {
viewModel.loadProfile()
}
}
Scope Types
Default Scope - Shared state across multiple navigations to same destination:
val entry = BackStackEntry(
destinationId = "profile",
scopeId = destination.defaultScope() // "entry:profile"
)
Unique Scope - Isolated state for each navigation:
val entry = BackStackEntry(
destinationId = "profile",
scopeId = newScope() // "entry:{randomUUID}"
)
Navigation Results
Pass data between destinations using results:
// Send result when popping
navController.pop(
result = ProfileResult(userId = "123"),
count = 1
)
// Receive result in destination
@Composable
fun HomeScreen(navController: NavController, entry: BackStackEntry) {
val result = entry.results["profile_result"] as? ProfileResult
LaunchedEffect(result) {
if (result != null) {
// Handle result
}
}
}
Custom Layouts & Transitions
Customize screen transitions and multi-pane layouts:
class MainNavigationGraph :
Related Skills
node-connect
347.6kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
108.4kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
347.6kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
347.6kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
