android-jetpack-compose

How to use NavController globally on Android JetPackCompose


I don't think my code is compact. Do I always have to hand over the Nav Controller to the argument in this way?

@Composable
fun ChatHome(
    navController: NavController,
    appViewModel: AppViewModel
) {
    ChatList(navController)
}


@Composable
fun ChatList(navController: NavController) {
    LazyColumn(
        modifier = Modifier
            .fillMaxSize()
            .padding(horizontal = 16.dp, vertical = 8.dp)
    ) {
        items(chatItemList) { chat ->
            ChatItem(chat, navController) {}
        }
    }
}

@Composable
fun ChatItem(
    chat: Chat,
    navController: NavController,
    onRowClick: (Chat) -> Unit
) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(10.dp)
            .clickable(onClick = {
                onRowClick(chat)
                navController.navigate("chatDetail/${chat.chatId}")
            })
    )
...

I wonder how to use Nav Controller globally.


Solution

  • Note: If you wan't preview-able composable, you can write own wrapper:

    // For single component
    @Composable
    fun ComposablePreview(content: @Composable () -> Unit) {
        CompositionLocalProvider(
            LocalNavigation provides rememberNavController(),
            content = content,
        )
    }
    
    // For theme
    @Composable
    fun AppThemePreview(
        darkTheme: Boolean = isSystemInDarkTheme(),
        content: @Composable () -> Unit,
    ) {
        CompositionLocalProvider(
            LocalNavigation provides rememberNavController()
        ) {
            AppTheme(darkTheme, content)
        }
    }
    
    // Usage
    @Preview
    @Composable
    fun ComponentPreview() {
        ComposablePreview {
           // Some composable
        }
    }
    
    @Preview
    @Composable
    fun ComponentPreviewWithTheme() {
        AppThemePreview {
           // Some composable
        }
    }
    

    Original answer:

    You can use CompositionLocalProvider.

    For example:

    Define our CompositionLocal with NavHostController

    val LocalNavigation = staticCompositionLocalOf<NavHostController> { error("Not provided") }
    

    Wrap your whole project with CompositionLocalProvider

    val navController = rememberNavController()
    CompositionLocalProvider(
        LocalNavigation provides navController,
    ) {
        NavHost(navController = navController, ...) { ... }
    }
    

    And now you can call it like that:

    @Composable
    fun ChatItem(
        chat: Chat,
        onRowClick: (Chat) -> Unit
    ) {
        val navController = LocalNavigation.current
    
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .padding(10.dp)
                .clickable(onClick = {
                    onRowClick(chat)
                    navController.navigate("chatDetail/${chat.chatId}")
                })
        )
    }