Jetpack Navigation 3 is a new Google navigation library that is fundamentally different from previous versions. The main idea of Nav3 is simple: you have a NavBackStack — a regular mutable list where each element represents a screen in your application.Jetpack Navigation 3 is a new Google navigation library that is fundamentally different from previous versions. The main idea of Nav3 is simple: you have a NavBackStack — a regular mutable list where each element represents a screen in your application.

Nav3 Router: Convenient Navigation on Top of Jetpack Navigation 3

2025/09/17 20:00
10 min read
For feedback or concerns regarding this content, please contact us at crypto.news@mexc.com

What is Jetpack Navigation 3?

Jetpack Navigation 3 is a new Google navigation library that is fundamentally different from previous versions. The main idea of Nav3 is simple: you have a NavBackStack — a regular mutable list where each element represents a screen in your application.

\ You add and remove elements from this list, and the UI automatically updates. Each screen is represented as a NavKey — a regular Kotlin class.

\ This gives you full control over navigation, but requires writing quite a lot of boilerplate code for typical operations.

Why Working Directly with NavBackStack is Inconvenient

Let's look at what the code looks like when working directly with NavBackStack:

@Composable fun MyApp() {     val backStack = rememberNavBackStack(Screen.Home)      // Add a screen     backStack.add(Screen.Details("123"))      // Go back     backStack.removeLastOrNull()      // Replace current screen     backStack.set(backStack.lastIndex, Screen.Success) } 

Problems begin when you need to trigger navigation from a ViewModel. You'll have to either pass NavBackStack to the ViewModel (which, in my understanding, violates architectural principles, as I believe the ViewModel shouldn't know about Compose-specific things), or create intermediate callbacks for each navigation action.

\ Additionally, when working with the stack directly, it's easy to forget to handle edge cases.

How Nav3 Router Simplifies the Work

Nav3 Router is a thin wrapper over Navigation 3 that provides a familiar API for navigation. Instead of thinking about indices and list operations, you simply say "go to screen X" or "go back."

\ Important point: Nav3 Router doesn't create its own stack. It works with the same NavBackStack that Navigation 3 provides, just making it more convenient to work with. When you call router.push(Screen.Details), the library translates this into the corresponding operation with the original stack.

\ Main advantages:

  • Can be used from ViewModel
  • Navigation commands are buffered if UI is temporarily unavailable (for example, during screen rotation)
  • All stack operations happen atomically
  • Clear API
  • Flexibility in modification and adding custom behavior

Installation

Nav3 Router is available on Maven Central. Add the dependency to your build.gradle.kts:

// For shared module in KMP project kotlin {     sourceSets {         commonMain.dependencies {             implementation("io.github.arttttt.nav3router:nav3router:1.0.0")         }     } }  // For Android-only project dependencies {     implementation("io.github.arttttt.nav3router:nav3router:1.0.0") } 

\ The library source code is available on GitHub: github.com/arttttt/Nav3Router

How Nav3 Router is Structured

The library consists of three main parts, each solving its own task:

Router — Developer Interface

Router provides methods like push(), pop(), replace(). When you call these methods, Router creates corresponding commands and sends them down the chain. Router itself knows nothing about how navigation will be executed — this allows using it from anywhere.

CommandQueue — Buffer Between Commands and Their Execution

CommandQueue solves the timing problem. Imagine: the user pressed a button during screen rotation. The UI is being recreated, and the navigator is temporarily unavailable. CommandQueue will save the command and execute it as soon as the navigator is ready again. Without this, the command would simply be lost.

// Simplified queue logic class CommandQueue<T : Any> {     private var navigator: Navigator<T>? = null     private val pending = mutableListOf<Command<T>>()      fun executeCommand(command: Command<T>) {         if (navigator != null) {             navigator.apply(command)  // Navigator exists - execute immediately         } else {             pending.add(command)       // No - save for later         }     } } 

Navigator takes commands and applies them to NavBackStack. Important detail: it first creates a copy of the current stack, applies all commands to it, and only then atomically replaces the original stack with the modified copy. This guarantees that the UI will never see intermediate stack states.

// Simplified Navigator logic fun applyCommands(commands: Array<Command>) {     val stackCopy = backStack.toMutableList()  // Work with a copy      for (command in commands) {         when (command) {             is Push -> stackCopy.add(command.screen)             is Pop -> stackCopy.removeLastOrNull()             // ... other commands         }     }      backStack.swap(stackCopy)  // Atomically apply changes } 

Getting Started With Nav3 Router

The simplest way — don't even create Router manually. Nav3Host will do it for you:

@Composable fun App() {     val backStack = rememberNavBackStack(Screen.Home)      // Nav3Host will create Router automatically     Nav3Host(backStack = backStack) { backStack, onBack, router ->         NavDisplay(             backStack = backStack,             entryProvider = entryProvider {                 entry<Screen.Home> {                     HomeScreen(                              router.push(Screen.Details)  // Use router                         }                     )                 }                  entry<Screen.Details> {                     DetailsScreen( router.pop() }                     )                 }             }         )     } } 

For more complex applications, it makes sense to create a Router through DI and pass it to the ViewModel:

\ Define screens

@Serializable sealed interface Screen : NavKey {     @Serializable     data object Home : Screen      @Serializable     data class Product(val id: String) : Screen      @Serializable     data object Cart : Screen } 

\ Pass router to Nav3Host.

@Composable fun App() {     val backStack = rememberNavBackStack(Screen.Home)     val router: Router<Screen> = getSomehowUsingDI()      // Pass Router to Nav3Host     Nav3Host(         backStack = backStack,         router = router,     ) { backStack, onBack, _ ->         NavDisplay(             backStack = backStack,             entryProvider = entryProvider {                 entry<Screen.Home> { HomeScreen() }                 entry<Screen.Details> { DetailsScreen() }             }         )     } } 

\ ViewModel receives Router through constructor.

class ProductViewModel(     private val router: Router<Screen>,     private val cartRepository: CartRepository ) : ViewModel() {      fun addToCart(productId: String) {         viewModelScope.launch {             cartRepository.add(productId)             router.push(Screen.Cart)  // Navigation from ViewModel         }     } } 

\ In UI, just use ViewModel.

@Composable fun ProductScreen(viewModel: ProductViewModel = koinViewModel()) {     Button(onClick = { viewModel.addToCart(productId) }) {         Text("Add to Cart")     } } 

Examples of Typical Scenarios

Simple Forward-Back Navigation

// Navigate to a new screen router.push(Screen.Details(productId))  // Go back router.pop()  // Navigate with replacement of current screen (can't go back) router.replaceCurrent(Screen.Success) 

Working with Screen Chains

// Open multiple screens at once router.push(     Screen.Category("electronics"),     Screen.Product("laptop-123"),     Screen.Reviews("laptop-123") )  // Return to a specific screen // Will remove all screens above Product from the stack router.popTo(Screen.Product("laptop-123")) 

Checkout Scenario

@Composable fun CheckoutScreen(router: Router<Screen>) {     Button(             // After checkout we need to:             // 1. Show confirmation screen             // 2. Prevent going back to cart              router.replaceStack(                 Screen.Home,                 Screen.OrderSuccess(orderId)             )             // Now only Home and OrderSuccess are in the stack         }     ) {         Text("Place Order")     } } 

Exiting Nested Navigation

// User is deep in settings: // Home -> Settings -> Account -> Privacy -> DataManagement  // "Done" button should return to home Button(         // Will leave only root (Home)         router.clearStack()     } ) {     Text("Done") }  // Or if you need to close the app from anywhere Button(         // Will leave only current screen and trigger system back         router.dropStack()     } ) {     Text("Exit") } 

Bonus: SceneStrategy and Dialogs

So far, we've only talked about simple navigation between screens. But what if you need to show a dialog or bottom sheet? This is where the SceneStrategy concept from Navigation 3 comes to help.

What is SceneStrategy?

SceneStrategy is a mechanism that determines exactly how screens from your stack will be displayed. By default, Navigation 3 uses SinglePaneSceneStrategy, which simply shows the last screen from the stack. But you can create your own strategies for more complex scenarios.

\ Think of SceneStrategy as a director who looks at your stack of screens and decides: "Okay, these three screens we show normally, but this last one — as a modal window on top of the previous ones". This allows representing different UI patterns with the same stack.

Creating a Strategy for ModalBottomSheet

Let's create a strategy that will show certain screens as bottom sheets. First, let's define how we'll mark such screens:

@Serializable sealed interface Screen : NavKey {     @Serializable     data object Home : Screen      @Serializable     data class Product(val id: String) : Screen      // This screen will be shown as bottom sheet     @Serializable     data object Filters : Screen } 

\ Now, let's create the strategy itself. It will check the metadata of the last screen and, if it finds a special marker, show it as a bottom sheet:

class BottomSheetSceneStrategy<T : Any> : SceneStrategy<T> {      companion object {         // Metadata key by which we identify bottom sheet         private const val BOTTOM_SHEET_KEY = "bottomsheet"          // Helper function to create metadata         fun bottomSheet(): Map<String, Any> {             return mapOf(BOTTOM_SHEET_KEY to true)         }     }      @Composable     override fun calculateScene(         entries: List<NavEntry<T>>,         onBack: (Int) -> Unit     ): Scene<T>? {         val lastEntry = entries.lastOrNull() ?: return null          // Check if the last screen has bottom sheet marker         val isBottomSheet = lastEntry.metadata[BOTTOM_SHEET_KEY] as? Boolean          if (isBottomSheet == true) {             // Return special Scene for bottom sheet             return BottomSheetScene(                 entry = lastEntry,                 previousEntries = entries.dropLast(1),             )         }          // This is not a bottom sheet, let another strategy handle it         return null     } } 

Combining Multiple Strategies

In a real application, you might need bottom sheets, dialogs, and regular screens. For this, you can create a delegate strategy that will choose the right strategy for each screen:

class DelegatedScreenStrategy<T : Any>(     private val strategyMap: Map<String, SceneStrategy<T>>,     private val fallbackStrategy: SceneStrategy<T> ) : SceneStrategy<T> {      @Composable     override fun calculateScene(         entries: List<NavEntry<T>>,         onBack: (Int) -> Unit     ): Scene<T>? {         val lastEntry = entries.lastOrNull() ?: return null          // Check all keys in metadata         for (key in lastEntry.metadata.keys) {             val strategy = strategyMap[key]             if (strategy != null) {                 // Found suitable strategy                 return strategy.calculateScene(entries, onBack)             }         }          // Use default strategy         return fallbackStrategy.calculateScene(entries, onBack)     } } 

Using in Application

Now, let's put it all together. Here's what using bottom sheet looks like in a real application:

@Composable fun ShoppingApp() {     val backStack = rememberNavBackStack(Screen.Home)     val router = rememberRouter<Screen>()      Nav3Host(         backStack = backStack,         router = router     ) { backStack, onBack, router ->         NavDisplay(             backStack = backStack,             // Use our combined strategy             sceneStrategy = DelegatedScreenStrategy(                 strategyMap = mapOf(                     "bottomsheet" to BottomSheetSceneStrategy(),                     "dialog" to DialogSceneStrategy()  // Navigation 3 already has this strategy                 ),                 fallbackStrategy = SinglePaneSceneStrategy()  // Regular screens             ),             entryProvider = entryProvider {                 entry<Screen.Home> {                     HomeScreen(                             // Open filters as bottom sheet                             router.push(Screen.Filters)                         }                     )                 }                  entry<Screen.Product> { screen ->                     ProductScreen(productId = screen.id)                 }                  // Specify that Filters should be bottom sheet                 entry<Screen.Filters>(                     metadata = BottomSheetSceneStrategy.bottomSheet()                 ) {                     FiltersContent( filters ->                             // Apply filters and close bottom sheet                             applyFilters(filters)                             router.pop()                         }                     )                 }             }         )     } } 

\ What's happening here? When you call router.push(Screen.Filters), the screen is added to the stack as usual. But thanks to metadata and our strategy, the UI understands that this screen needs to be shown as a bottom sheet on top of the previous screen, rather than replacing it completely.

\ When calling router.pop() the bottom sheet will close, and you'll return to the previous screen. From Router's point of view, this is regular back navigation, but visually it looks like closing a modal window.

Advantages of This Approach

Using SceneStrategy provides several important advantages. First, your navigation logic remains simple — you still use push and pop without thinking about how exactly the screen will be shown. Second, navigation state remains consistent — bottom sheet is just another screen in the stack that is properly saved during screen rotation or process kill. And finally, it provides great flexibility — you can easily change how a screen is displayed by just changing its metadata, without touching the navigation logic.

\ This approach is especially useful when the same screen can be shown differently depending on context. For example, a login screen can be a regular screen on first app launch and a modal dialog when attempting to perform an action that requires authorization.

Why You Should Use Nav3 Router

Nav3 Router doesn't try to replace Navigation 3 or add new features. Its task is to make working with navigation convenient and predictable. You get a simple API that can be used from any layer of the application, automatic handling of timing issues, and the ability to easily test navigation logic.

\ At the same time, under the hood, regular Navigation 3 still works with all its capabilities: state saving, animation support, and proper handling of the system "Back" button.

\ If you're already using Navigation 3 or planning to migrate to it, Nav3 Router will help make this experience more pleasant without adding unnecessary complexity to the project.

  • GitHub repository: github.com/arttttt/Nav3Router
  • Usage examples: see sample folder in the repository

\

Market Opportunity
TOP Network Logo
TOP Network Price(TOP)
$0.0000697
$0.0000697$0.0000697
0.00%
USD
TOP Network (TOP) Live Price Chart
Disclaimer: The articles reposted on this site are sourced from public platforms and are provided for informational purposes only. They do not necessarily reflect the views of MEXC. All rights remain with the original authors. If you believe any content infringes on third-party rights, please contact crypto.news@mexc.com for removal. MEXC makes no guarantees regarding the accuracy, completeness, or timeliness of the content and is not responsible for any actions taken based on the information provided. The content does not constitute financial, legal, or other professional advice, nor should it be considered a recommendation or endorsement by MEXC.

You May Also Like

Revolutionary: CME SOL XRP Futures Options Set to Transform Crypto Trading

Revolutionary: CME SOL XRP Futures Options Set to Transform Crypto Trading

BitcoinWorld Revolutionary: CME SOL XRP Futures Options Set to Transform Crypto Trading Exciting news is rippling through the cryptocurrency world! The U.S. Chicago Mercantile Exchange (CME), a titan in traditional finance, is reportedly planning to launch CME SOL XRP futures options. This significant development, initially reported by Walter Bloomberg, marks a pivotal moment for institutional involvement in the altcoin market. It signals a new era for how Solana (SOL) and Ripple (XRP) might be traded, potentially opening doors to broader adoption and increased market maturity. What Does the Launch of CME SOL XRP Futures Mean for Crypto? When an institution like CME, known for its rigorous standards and vast trading volume, enters a new market, it brings a wave of legitimacy. The introduction of CME SOL XRP futures options indicates a growing acceptance of these digital assets within mainstream finance. This move could fundamentally change how investors perceive and interact with SOL and XRP. Futures options are financial derivatives that give traders the right, but not the obligation, to buy or sell an underlying asset at a specific price on or before a certain date. For SOL and XRP, this means: Enhanced Price Discovery: More participants and trading volume can lead to more efficient and accurate pricing. Institutional Access: It provides regulated avenues for large institutional investors to gain exposure to SOL and XRP without directly owning the underlying assets. Risk Management: Traders can use these options to hedge against potential price fluctuations in their existing SOL and XRP holdings. Why Are SOL and XRP Chosen for CME SOL XRP Futures? The selection of Solana (SOL) and Ripple (XRP) for these new futures options is not arbitrary. Both cryptocurrencies hold significant positions in the market and offer distinct value propositions: Solana (SOL): Known for its high-performance blockchain, offering fast transaction speeds and low costs. Its robust ecosystem supports numerous decentralized applications (dApps), NFTs, and DeFi projects, attracting considerable developer and user interest. Ripple (XRP): Primarily focused on facilitating fast, low-cost international payments for financial institutions. Despite ongoing regulatory discussions, XRP maintains a strong market presence and a dedicated community, highlighting its potential for cross-border transactions. Their substantial market capitalization and existing liquidity make them attractive candidates for institutional-grade derivative products. This choice reflects a strategic assessment by CME of assets that can sustain significant trading interest and volume. Navigating the Landscape: Opportunities and Considerations for CME SOL XRP Futures The introduction of CME SOL XRP futures options presents a wealth of opportunities, yet it also comes with important considerations. On the opportunity front, we can expect increased liquidity, which benefits all market participants by making it easier to buy and sell without significant price impact. Moreover, it could attract new capital from traditional financial players who prefer regulated products. However, traders and investors should also consider the implications: Market Volatility: While derivatives can offer hedging, they can also amplify market movements. Regulatory Clarity: The regulatory landscape for cryptocurrencies, particularly for XRP, continues to evolve. CME’s move might encourage further clarity but also means ongoing scrutiny. Learning Curve: Understanding futures options requires a certain level of financial literacy, which new entrants to the crypto market may need to develop. These products offer sophisticated tools for managing exposure and speculating on price movements, but they demand a careful approach. What’s Next for the Crypto Market with CME SOL XRP Futures? The reported launch of CME SOL XRP futures options is more than just a new product offering; it represents a significant milestone in the ongoing convergence of traditional finance and the digital asset space. It underscores the growing maturity of the cryptocurrency market and its increasing integration into global financial systems. As institutional interest continues to surge, we can anticipate further innovation and a broader range of regulated products for other altcoins. This development is poised to offer sophisticated tools for investors and traders, potentially stabilizing market dynamics while simultaneously introducing new avenues for growth and investment. The crypto market is evolving rapidly, and CME’s latest initiative is a clear indicator of this exciting trajectory. To learn more about the latest crypto market trends, explore our article on key developments shaping the cryptocurrency market institutional adoption. Frequently Asked Questions (FAQs) What is the Chicago Mercantile Exchange (CME)? The CME is one of the world’s largest and most diverse derivatives marketplaces, offering a wide range of futures and options products across various asset classes, including equities, commodities, and now, expanding into specific cryptocurrencies. What are futures options in the context of SOL and XRP? Futures options for SOL and XRP are financial contracts that give the holder the right, but not the obligation, to buy or sell SOL or XRP futures contracts at a predetermined price on or before a specific date. They allow for hedging and speculation on price movements. Why are Solana (SOL) and Ripple (XRP) chosen for these new options? SOL and XRP were likely chosen due to their significant market capitalization, established liquidity, and distinct use cases within the crypto ecosystem, making them attractive for institutional-grade derivative products. How might CME SOL XRP futures options affect the prices of SOL and XRP? The introduction of these options could lead to increased liquidity and institutional participation, potentially influencing price discovery and stability. However, like all derivatives, they can also contribute to market volatility. When are these CME SOL XRP futures options expected to launch? While Walter Bloomberg reported CME’s plans, an official launch date has not yet been publicly announced by CME. Market participants should monitor official CME channels for updates. If you found this article insightful, please consider sharing it with your network! Help us spread the word about the exciting developments in the crypto space by sharing this article on your social media platforms. This post Revolutionary: CME SOL XRP Futures Options Set to Transform Crypto Trading first appeared on BitcoinWorld.
Share
Coinstats2025/09/18 00:45
Gold Hits $3,700 as Sprott’s Wong Says Dollar’s Store-of-Value Crown May Slip

Gold Hits $3,700 as Sprott’s Wong Says Dollar’s Store-of-Value Crown May Slip

The post Gold Hits $3,700 as Sprott’s Wong Says Dollar’s Store-of-Value Crown May Slip appeared on BitcoinEthereumNews.com. Gold is strutting its way into record territory, smashing through $3,700 an ounce Wednesday morning, as Sprott Asset Management strategist Paul Wong says the yellow metal may finally snatch the dollar’s most coveted role: store of value. Wong Warns: Fiscal Dominance Puts U.S. Dollar on Notice, Gold on Top Gold prices eased slightly to $3,678.9 […] Source: https://news.bitcoin.com/gold-hits-3700-as-sprotts-wong-says-dollars-store-of-value-crown-may-slip/
Share
BitcoinEthereumNews2025/09/18 00:33
Will XRP Price Increase In September 2025?

Will XRP Price Increase In September 2025?

Ripple XRP is a cryptocurrency that primarily focuses on building a decentralised payments network to facilitate low-cost and cross-border transactions. It’s a native digital currency of the Ripple network, which works as a blockchain called the XRP Ledger (XRPL). It utilised a shared, distributed ledger to track account balances and transactions. What Do XRP Charts Reveal? […]
Share
Tronweekly2025/09/18 00:00