Jetpack Compose中使用Navigation导航的两种方式

Jetpack Compose中使用Navigation导航的两种方式

Jetpack Compose中使用Navigation导航的两种方式_第1张图片
这里介绍两种方式

  1. 直接从屏幕导航
  2. 使用 ViewModel 导航

直接从Screen导航

设置

添加依赖项

dependencies { 
    def lifecycle_version = "2.6.0-alpha03"

    // Pass the ViewModel directly to the screen
    implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
    implementation "androidx.lifecycle:lifecycle-runtime-compose:$lifecycle_version"
}

设置导航

让我们创建一个Navigation.kt文件 ,我们将在其中设置导航。

@Composable
fun Navigation() {
    val navController = rememberNavController()

    NavHost(
        navController = navController,
        // We need to create the routes first
        startDestination = ""
    ) {
        /* ... */
    }
}

NavController— 允许我们导航到其他屏幕并跟踪可组合项的返回堆栈。

NavHost— 将NavController与导航图链接,该导航图指定您应该能够在屏幕之间导航的可组合目的地。

创建路由

创建路由的方法有多种,但我会向您展示我喜欢的一种。首先,我创建了一个名为Screen.kt的文件,并将它们存储在一个密封类中。

sealed class Screen(val route: String) {
    object Home : Screen(route = "home")
    object Favorites : Screen(route = "favorites")
    object Cart : Screen(route = "cart")
}

将屏幕添加到 NavHost

@Composable
fun Navigation() {
    val navController = rememberNavController()

    NavHost(
        navController = navController,
        startDestination = Screen.Home.route
    ) {
        composable(route = Screen.Home.route) {
            /* ... */
        }
        composable(route = Screen.Favorites.route) {
            /* ... */
        }
        composable(route = Screen.Cart.route) {
            /* ... */
        }
    }
}

创建屏幕

@Composable
fun HomeScreen(
    navigate: () -> Unit
) {
    Column {
        Text(text = "Home")
        Button(onClick = navigate) {
            Text(text = "Navigate to favorites")
        }
    }
}

@Composable
fun FavoritesScreen(
    navigateBack: () -> Unit,
    navigate: () -> Unit
) {
    Column {
        Text(text = "Favorites")
        Button(onClick = navigateBack) {
            Text(text = "Navigate back")
        }
        Button(onClick = navigate) {
            Text(text = "Navigate to cart")
        }
    }
}

@Composable
fun CartScreen(navigateBackToHome: () -> Unit) {
    Column {
        Text(text = "Cart")
        Button(onClick = navigateBackToHome) {
            Text(text = "Navigate back to home")
        }
    }
}

建议不要共享NavController直达屏幕,您应该只调用navigate()作为回调的一部分而不是可组合项本身,以避免调用navigate()每次重组。

@Composable
fun Navigation() {
    val navController = rememberNavController()

    NavHost(
        navController = navController,
        startDestination = Screen.Home.route
    ) {
        composable(route = Screen.Home.route) {
            HomeScreen(
                navigate = {
                    navController.navigate(Screen.Favorites.route) {
                        // If it is true, multiple copies won't be created
                        launchSingleTop = true
                    }
                }
            )
        }
        composable(route = Screen.Favorites.route) {
            FavoritesScreen(
                navigateBack = {
                    // Navigate back
                    navController.popBackStack()
                },
                navigate = {
                    navController.navigate(Screen.Cart.route) {
                        launchSingleTop = true
                    }
                }
            )
        }
        composable(route = Screen.Cart.route) {
            CartScreen(
                navigateBackToHome = {
                    // Navigate back to Home screen
                    navController.popBackStack(
                        route = Screen.Home.route,
                        // If this is true, the destination will be removed
                        inclusive = false
                    )
                }
            )
        }
    }
}

使用 ViewModel 导航

使用 ViewModel 导航
如果 UI 很复杂,您应该使用此方法。

设置

将这些依赖项添加到您的应用程序build.gradle文件中。

dependencies { 
    def lifecycle_version = "2.6.0-alpha03"

    // Pass the ViewModel directly to the screen
    implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
    implementation "androidx.lifecycle:lifecycle-runtime-compose:$lifecycle_version"
}

创建一个名为 UiEvent.kt 密封接口,这是我们的应用程序事件。

sealed interface UiEvent {
    data class Navigate(val route: String): UiEvent
    data class PopBackStack(val route: String? = null, val inclusive: Boolean = false): UiEvent
}

让我们分析一下我们的Screen、ViewModel 和Event

// Screen events
sealed interface HomeScreenEvents {
    object OnNavigateToFavorites : HomeScreenEvents
}

class HomeViewModel : ViewModel() {

    // Creating a channel within which we send events
    private val _uiEvent: Channel<UiEvent> = Channel()
    // Convert the received values into a flow to collect it's data
    val uiEvent = _uiEvent.receiveAsFlow()

    // Helper function to remove the boilerplate code
    private fun sendEvent(event: UiEvent) {
        viewModelScope.launch {
            _uiEvent.send(event)
        }
    }

    fun onEvent(event: HomeScreenEvents) {
        // We have multiple action cases
        when (event) {
            HomeScreenEvents.OnNavigateToFavorites -> {
                // Sends an UiEvent of type Navigation with a route in it
                sendEvent(UiEvent.Navigate(route = Screen.Favorites.route))
            }
        }
    }
}

@Composable
fun HomeScreen(
    navigate: (UiEvent.Navigate) -> Unit,
    // ViewModel is passed by the lifecycle
    viewModel: HomeViewModel = viewModel()
) {
    LaunchedEffect(key1 = true) {
        // Collects the latest event from the channel
        viewModel.uiEvent.collectLatest { event ->
            when (event) {
                is UiEvent.Navigate -> {
                    navigate(event)
                }

                else -> Unit
            }
        }
    }

    Column {
        Text(text = "Home")
        Button(
            onClick = {
                // Sending to the onEvent function an event
                viewModel.onEvent(HomeScreenEvents.OnNavigateToFavorites)
            }
        ) {
            Text(text = "Navigate to favorites")
        }
    }
}
// Screen events
sealed interface FavoritesScreenEvents {
    object OnNavigateToCart : FavoritesScreenEvents
    object OnNavigateBack : FavoritesScreenEvents
}

class FavoritesViewModel : ViewModel() {

    // Creating a channel within which we send events
    private val _uiEvent: Channel<UiEvent> = Channel()
    // Convert the received values into a flow to collect it's data
    val uiEvent = _uiEvent.receiveAsFlow()

    // Helper function to remove the boilerplate code
    private fun sendEvent(event: UiEvent) {
        viewModelScope.launch {
            _uiEvent.send(event)
        }
    }
    
    fun onEvent(event: FavoritesScreenEvents) {
        when (event) {
            FavoritesScreenEvents.OnNavigateBack -> {
                // Sends an UiEvent to navigate back
                sendEvent(UiEvent.PopBackStack())
            }

            FavoritesScreenEvents.OnNavigateToCart -> {
                // Sends an UiEvent to navigate to cart screen
                sendEvent(UiEvent.Navigate(route = Screen.Cart.route))
            }
        }
    }
}

@Composable
fun FavoritesScreen(
    // ViewModel is passed by the lifecycle
    viewModel: FavoritesViewModel = viewModel(),
    navigateBack: () -> Unit,
    navigate: (UiEvent.Navigate) -> Unit
) {
    LaunchedEffect(key1 = true) {
        // Collects the latest event from the channel
        viewModel.uiEvent.collectLatest { event ->
            when (event) {
                is UiEvent.Navigate -> {
                    navigate(event)
                }

                is UiEvent.PopBackStack -> {
                    navigateBack()
                }
            }
        }
    }

    Column {
        Text(text = "Favorites")
        Button(
            onClick = {
                viewModel.onEvent(FavoritesScreenEvents.OnNavigateBack)
            }
        ) {
            Text(text = "Navigate back")
        }
        Button(
            onClick = {
                viewModel.onEvent(FavoritesScreenEvents.OnNavigateToCart)
            }
        ) {
            Text(text = "Navigate to cart")
        }
    }
}
// Screen events
sealed interface CartScreenEvents {
    object OnNavigateBackToHome : CartScreenEvents
}

class CartViewModel : ViewModel() {

    // Creating a channel within which we send events
    private val _uiEvent: Channel<UiEvent> = Channel()
    // Convert the received values into a flow to collect it's data
    val uiEvent = _uiEvent.receiveAsFlow()

    // Helper function to remove the boilerplate code
    private fun sendEvent(event: UiEvent) {
        viewModelScope.launch {
            _uiEvent.send(event)
        }
    }

    fun onEvent(event: CartScreenEvents) {
        when (event) {
            CartScreenEvents.OnNavigateBackToHome -> {
                sendEvent(
                    UiEvent.PopBackStack(
                        // Pop to this route
                        route = Screen.Home.route,
                        // If this is true, the destination will be popped
                        inclusive = false
                    )
                )
            }
        }
    }
}

@Composable
fun CartScreen(
    // ViewModel is passed by the lifecycle
    viewModel: CartViewModel = viewModel(),
    navigateBack: (UiEvent.PopBackStack) -> Unit
) {
    LaunchedEffect(key1 = true) {
        // Collects the latest event from the channel
        viewModel.uiEvent.collectLatest { event ->
            when (event) {
                is UiEvent.PopBackStack -> {
                    navigateBack(event)
                }

                else -> Unit
            }
        }
    }
    Column {
        Text(text = "Cart")
        Button(
            onClick = {
                viewModel.onEvent(CartScreenEvents.OnNavigateBackToHome)
            }
        ) {
            Text(text = "Navigate back to home")
        }
    }
}

LaunchedEffect 是一个 Side-effect

@Composable
fun Navigation() {
    val navController = rememberNavController()

    NavHost(
        navController = navController,
        startDestination = Screen.Home.route
    ) {
        composable(route = Screen.Home.route) {
            HomeScreen(
                navigate = { destination ->
                    navController.navigate(destination.route) {
                        launchSingleTop = true
                    }
                }
            )
        }
        composable(route = Screen.Favorites.route) {
            FavoritesScreen(
                navigateBack = {
                    navController.popBackStack()
                },
                navigate = { destination ->
                    navController.navigate(destination.route) {
                        launchSingleTop = true
                    }
                }
            )
        }
        composable(route = Screen.Cart.route) {
            CartScreen(
                navigateBack = { destination ->
                    // In a bigger project you can either
                    // pop up to a specific screen or to the previous screen
                    if (destination.route == null) {
                        navController.popBackStack()
                    } else {
                        navController.popBackStack(
                            route = destination.route,
                            inclusive = destination.inclusive
                        )
                    }
                }
            )
        }
    }
}

参考

https://medium.com/@daniel.atitienei/navigation-in-jetpack-compose-88a92c40e98b#055f

你可能感兴趣的:(Android开源框架,Android架构,android,jetpack,android)