Compose : Navigation

Skele·2024년 6월 4일
0

Android

목록 보기
11/15

NavController is central to navigation, thus, created first.

val navController = rememberNavController()

It can be obtained by calling rememberNavController(). NavController should be created and place at the top level of the hierarchy being the single source of truth for navigation and managing back stack.

Container for destinations and displays current destination of the nav graph.
Use NavController to navigate between composables, and the NavHost recomposes as the destination changes.

Route

Unique string that represents destination in navigation graph. Compose use serializable object or class to define a route. Navigation graph is created with composable and routes.
Route can be added using Navigation.composable extension function.

String as Route

interface RallyDestination {
    val icon: ImageVector
    val route: String
}

object Overview : RallyDestination {
    override val icon = Icons.Filled.PieChart
    override val route = "overview"
}
NavHost(
    navController = navController,
    startDestination = Overview.route,
    modifier = Modifier.padding(innerPadding)
) { // this : NavGraphBuilder
    composable(route = Overview.route){
        OverviewScreen()
    }
    composable(route = Accounts.route) {
        AccountsScreen()
    }
    composable(route = Bills.route) {
        BillsScreen()
    }
}

Object as Route

@Serializable
object Profile
@Serializable
object FriendsList

val navController = rememberNavController()

NavHost(
	navController = navController, 
	startDestination = Profile,
) {
    composable<Profile> { ProfileScreen( /* ... */ ) }
    composable<FriendsList> { FriendsListScreen( /* ... */ ) }
    // Add more destinations similarly.
}
val navGraph by remember(navController) {
  navController.createGraph(startDestination = Profile)) {
    composable<Profile> { ProfileScreen( /* ... */ ) }
    composable<FriendsList> { FriendsListScreen( /* ... */ ) }
  }
}
NavHost(navController, navGraph)
@Serializable
object Profile
@Serializable
object FriendsList

@Composable
fun MyAppNavHost(
    modifier: Modifier = Modifier,
    navController: NavHostController = rememberNavController(),

) {
    NavHost(
        modifier = modifier,
        navController = navController,
        startDestination = Profile
    ) {
        composable<Profile> {
            ProfileScreen(
            	// callback for navigation
                onNavigateToFriends = { navController.navigate(route = FriendsList) },
                /*...*/
            )
        }
        composable<FriendsList> { FriendsListScreen(/*...*/) }
    }
}

@Composable
fun ProfileScreen(
    onNavigateToFriends: () -> Unit,
    /*...*/
) {
    /*...*/
    Button(onClick = onNavigateToFriends) {
        Text(text = "See friends list")
    }
}

Provide callbacks for the exact navigation and do not pass NavControllers directly to keep the code testable and reusable.

Argument can be passed when navigating by appending it to the route in a form route/{argument}.

composable(
    route = SingleAccount.routeWithArgs, // "${SingleAccount.route}/{${SingleAccount.accountTypeArg}}"
    arguments = SingleAccount.arguments // list of navArguments
){ navBackStackEntry ->
    // Retrieve the passed argument
    val accountType =
        navBackStackEntry.arguments?.getString(SingleAccount.accountTypeArg)
    // Pass accountType to SingleAccountScreen
    SingleAccountScreen(accountType)
}
object SingleAccount : RallyDestination {
    override val route = "single_account"
    const val accountTypeArg = "account_type"
    val routeWithArgs = "${route}/{${accountTypeArg}}"
    val arguments = listOf(
        navArgument(name = accountTypeArg) { type = NavType.StringType }
    )
}
private fun NavHostController.navigateToSingleAccount(accountType: String) {
    this.navigateSingleTopTo("${SingleAccount.route}/$accountType")
}
onAccountClick = { accountType ->
    navController.navigateToSingleAccount(accountType)
}

navArgument
Creates an argument with the given name that is supported by NavDestination. In the builder, type is defined with an optional default value or nullable, if the type supports it, that is used to read or write it in the Bundle.
Argument can be retrieved from the navBackStackEntry with matching name.

Navigating with deeplink uses route/{argument} format with some addition.

<intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="rally" android:host="single_account" />
</intent-filter>

Add intent-filter to open the app and navigate to destination.

composable(
    route = SingleAccount.routeWithArgs,
    arguments = SingleAccount.arguments,
    deepLinks = SingleAccount.deepLinks
) {...}
object SingleAccount : RallyDestination {
    // ...
    val deepLinks = listOf(
       navDeepLink { uriPattern = "rally://$route/{$accountTypeArg}"}
    )
}

Deeplink can be added to the existing composable destination with the form of scheme://route/{argument}. Everything else is the same as using argument in navigation.

adb shell am start -d "rally://single_account/Checking" -a android.intent.action.VIEW

Deeplink can be tested with adb.

Flags

fun NavHostController.navigateSingleTopTo(route: String) =
    this.navigate(route) { 
        popUpTo(
            this@navigateSingleTopTo.graph.findStartDestination().id
        ) {
            saveState = true
        }
        launchSingleTop = true
        restoreState = true
}

SingleTop
Maintain only one copy of the destination on top of the back stack.

fun NavHostController.navigateSingleTopTo(route: String) =
this.navigate(route) { launchSingleTop = true }

PopUpTo
Pop up to the start destination of the graph.

{ popUpTo(startDestination) { saveState = true } }

restoreState
Whether to restore the state saved previously with saveState. If no state was saved, it has no effect.

{ restoreState = true }

currentBackStackEntryAsState()
Returns top entry of the back stack as mutable state.

val currentBackStatck by navController.currentBackStackEntryAsState()
val currentDestination = currentBackStatck?.destination

Testing Navigation

profile
Tireless And Restless Debugging In Source : TARDIS

0개의 댓글