navigation patterns

📁 fwrite0920/android-skills 📅 Jan 1, 1970
4
总安装量
0
周安装量
#49240
全站排名
安装命令
npx skills add https://github.com/fwrite0920/android-skills --skill 'Navigation Patterns'

Skill 文档

Navigation Patterns (导航模式)

Instructions

  • 确认需求属于导览与 Back Stack 管理
  • 依照下方章节顺序套用
  • 一次只处理一个导航面向(Deep Link、跨模块、Back Stack)
  • 完成后对照 Quick Checklist

When to Use

  • Scenario A:新项目导航设计
  • Scenario B:旧项目扩充与导航桥接

Example Prompts

  • “请参考 Type-Safe Args,更新我的 NavHost 写法”
  • “依照 Deep Links 章节,帮我加入 App Links”
  • “请用 Multi-Module Navigation 设计跨模块导航接口”

Workflow

  1. 先创建 Compose Navigation 基础路由
  2. 再加入 Deep Links 与跨模块导航
  3. 最后用 Back Stack 管理与 Quick Checklist 验收

Practical Notes (2026)

  • 默认采用 type-safe args,避免字符串路由散落
  • Deep Link 必须有验证与回归测试流程
  • Back Stack 规则统一化,避免各模块自订逻辑

Minimal Template

目标: 
路由范围: 
Deep Link: 
Back Stack 规则: 
验收: Quick Checklist

Compose Navigation Basics

Type-Safe Args (Navigation 2.8+)

// 定义路由
@Serializable
data class DetailRoute(val productId: String)

@Serializable
object HomeRoute

// NavHost
NavHost(navController, startDestination = HomeRoute) {
    composable<HomeRoute> { 
        HomeScreen(onProductClick = { id ->
            navController.navigate(DetailRoute(id))
        })
    }
    
    composable<DetailRoute> { backStackEntry ->
        val route = backStackEntry.toRoute<DetailRoute>()
        DetailScreen(productId = route.productId)
    }
}

Nested Graphs

NavHost(navController, startDestination = "main") {
    navigation(startDestination = "home", route = "main") {
        composable("home") { HomeScreen() }
        composable("profile") { ProfileScreen() }
    }
    
    navigation(startDestination = "login", route = "auth") {
        composable("login") { LoginScreen() }
        composable("register") { RegisterScreen() }
    }
}

Deep Links

App Links 设置

<!-- AndroidManifest.xml -->
<activity android:name=".MainActivity">
    <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data
            android:scheme="https"
            android:host="example.com"
            android:pathPrefix="/product" />
    </intent-filter>
</activity>

assetlinks.json (Host 验证)

// https://example.com/.well-known/assetlinks.json
[{
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
        "namespace": "android_app",
        "package_name": "com.example.app",
        "sha256_cert_fingerprints": ["..."]
    }
}]

Navigation 集成

composable(
    route = "product/{id}",
    deepLinks = listOf(
        navDeepLink { uriPattern = "https://example.com/product/{id}" }
    )
) { backStackEntry ->
    val id = backStackEntry.arguments?.getString("id")
    ProductScreen(id)
}

Multi-Module Navigation

API Module Pattern

// :feature:product:api
interface ProductNavigator {
    fun navigateToProduct(productId: String)
    fun navigateToProductList()
}

// :feature:product:impl
class ProductNavigatorImpl @Inject constructor(
    private val navController: NavController
) : ProductNavigator {
    override fun navigateToProduct(productId: String) {
        navController.navigate("product/$productId")
    }
}

// 其他模块使用
class HomeViewModel @Inject constructor(
    private val productNavigator: ProductNavigator
) {
    fun onProductClick(id: String) {
        productNavigator.navigateToProduct(id)
    }
}

Navigation Events (Single Event)

// ViewModel
sealed class NavigationEvent {
    data class ToDetail(val id: String) : NavigationEvent()
    object Back : NavigationEvent()
}

private val _navigationEvent = Channel<NavigationEvent>()
val navigationEvent = _navigationEvent.receiveAsFlow()

// Composable
LaunchedEffect(Unit) {
    viewModel.navigationEvent.collect { event ->
        when (event) {
            is NavigationEvent.ToDetail -> navController.navigate("detail/${event.id}")
            NavigationEvent.Back -> navController.popBackStack()
        }
    }
}

Complex Back Stack Management

Auth Flow (Clear Stack)

fun navigateToHome() {
    navController.navigate("home") {
        popUpTo("auth") { inclusive = true }  // 清除 auth 流程
        launchSingleTop = true
    }
}

Bottom Nav with Separate Stacks

@Composable
fun MainScreen() {
    val navController = rememberNavController()
    
    Scaffold(
        bottomBar = {
            NavigationBar {
                items.forEach { item ->
                    NavigationBarItem(
                        selected = currentRoute == item.route,
                        onClick = {
                            navController.navigate(item.route) {
                                popUpTo(navController.graph.findStartDestination().id) {
                                    saveState = true
                                }
                                launchSingleTop = true
                                restoreState = true
                            }
                        }
                    )
                }
            }
        }
    ) { /* NavHost */ }
}

Quick Checklist

  • 使用 Type-Safe Args (Navigation 2.8+)
  • Deep Links 配置 assetlinks.json
  • 跨模块使用 Navigator interface
  • Navigation Events 作为 Single Event 处理
  • Bottom Nav 正确保存/恢复 State