Foldable navigation
The foldable navigation component is a library built on top of the Android navigation component. It helps developers to implement fragment navigation for different screen modes or to adapt an existing application to Foldable Navigation pattern;
The foldable navigation component consists of three key parts:
- Navigation Graph - An XML resource that contains all navigation-related information in one centralized location. It is the same as the one from the navigation component provided by Google.
- FoldableNavHost - An empty container that displays destinations from your navigation graph. The implementation for the foldable navigation is
FoldableNavHostFragment
. - FoldableNavController - An object that manages app navigation within a
FoldableNavHost
.
Overview
Applications on dual-screen and foldable devices can be displayed on a single screen or spanned across a folding feature. When the application is first started:
- In single-screen only one fragment (A) will be visible.
- If the application is rendered across a folding feature, the first fragment (A) will be on the first side of the fold and the other side of the fold will be blank.
From the initial state, if you navigate to another fragment (B) then the new fragment will be opened on the end screen.
If the user navigates to a third fragment (C) it will be shown on the end screen, the previous fragment (B) will be moved on the start screen..
- When the app is moved from being spanned to a single screen, then all fragments from the end screen are moved to the start screen and (C) will appear on top.
- When the app is moved from a single screen to spanned across a fold or hinge, and the navigation stack has more than two fragments, then the last fragment will be moved to the end screen.
Change display area destination for actions
You can specify where a new fragment will be displayed using the launchScreen
attribute in the navigation graph.
Possible values for launchScreen
are:
start
- the fragment will be opened on the first screenend
- the fragment will be opened on the second screenboth
- the fragment will cover the entire display area
This navigation XML example shows how to use this attribute:
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/home"
app:startDestination="@+id/titleScreen">
<fragment
android:id="@+id/titleScreen"
android:name="com.microsoft.device.dualscreen.navigation.sample.homescreen.TitleFragment"
android:label="@string/title_home"
tools:layout="@layout/fragment_title">
<action
android:id="@+id/action_title_to_about"
app:launchScreen="end"
app:destination="@id/aboutScreen"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_right"
app:popExitAnim="@anim/slide_out_left" />
</fragment>
</navigation>
Important
This attribute can only be changed by editing the XML file directly. It cannot be modified using the Android Studio Editor.
Sample
You can download this navigation sample app to see all these behaviors.
How to import the library into your project
Add the the dependency to the module-level build.gradle file:
If your project is created using Java, you will need to add a kotlin-stdlib dependency to your module-level build.gradle file. (This is because some part of the library was created using Kotlin.)
dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" }
These components are built on top of the Navigation component provided by Google and thus the Foldable-Navigation library contains a dependency to that.
Create a navigation graph
A navigation graph is an xml resource file with all of your app's navigation paths, using destinations and actions. The navigation graph can be created via Android Studio Navigation Editor or manually via an XML editor. You can find more info at Create a navigation graph.
Add a NavHost to an activity
The foldable navigation component is designed for apps with one main activity and multiple fragment destinations. The main activity is associated with a navigation graph and will contain a FoldableNavHostFragment
that is responsible for swapping fragment destinations. If your app will have more than one activity, then each activity will have its own navigation graph.
This is an example main activity XML layout file, showing how to set the app:navGraph
attribute:
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/surface_duo_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.FoldableNavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>
The FoldableNavHost
can also be set programmatically:
val navHostFragment = FoldableNavHostFragment.create(navGraphId)
fragmentManager.beginTransaction()
.add(containerId, navHostFragment, fragmentTag)
.commitNow()
You can learn more about how to add the FoldableNavHost
at Add a NavHost to an activity.
Navigate to destination
This code snippet can be used to navigate fragments according to the foldable navigation rules:
class SomeFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById<Button>(R.id.btn_next).setOnClickListener {
findFoldableNavController().navigate(R.id.action_next)
}
}
}
Update UI components with FoldableNavigationUI
The FoldableNavigationUI
is a similar component like NavigationUI
in the Jetpack Navigation component and contains
static methods that manage navigation with the top app bar, the navigation drawer, and bottom navigation.
You can find more information about
FoldableNavigationUI
contains the following methods similar to those provided by NavigationUI
:
// same method name, with foldable parameter
boolean onNavDestinationSelected(MenuItem item, FoldableNavController navController)
boolean navigateUp(FoldableNavController navController, Openable openableLayout)
boolean navigateUp(FoldableNavController navController, FoldableAppBarConfiguration configuration)
// method name changed to reflect foldable navigation
void setupActionBarWithFoldableNavController(AppCompatActivity activity, FoldableNavController navController)
void setupActionBarWithFoldableNavController(AppCompatActivity activity, FoldableNavController navController, Openable openableLayout)
void setupActionBarWithFoldableNavController(AppCompatActivity activity, FoldableNavController navController, FoldableAppBarConfiguration configuration)
void setupWithFoldableNavController(Toolbar toolbar, FoldableNavController navController)
void setupWithFoldableNavController(Toolbar toolbar, FoldableNavController navController, Openable openableLayout)
void setupWithFoldableNavController(Toolbar toolbar, FoldableNavController navController, FoldableAppBarConfiguration configuration)
void setupWithFoldableNavController(CollapsingToolbarLayout collapsingToolbarLayout, Toolbar toolbar, FoldableNavController navController)
void setupWithFoldableNavController(CollapsingToolbarLayout collapsingToolbarLayout, Toolbar toolbar, FoldableNavController navController, Openable openableLayout)
void setupWithFoldableNavController(CollapsingToolbarLayout collapsingToolbarLayout, Toolbar toolbar, FoldableNavController navController, FoldableAppBarConfiguration configuration)
void setupWithFoldableNavController(NavigationView navigationView, FoldableNavController navController)
void setupWithFoldableNavController(BottomNavigationView bottomNavigationView, FoldableNavController navController)
Migrate existing applications to foldable navigation
Existing applications that use the Navigation component provided by Google can add foldable functionality by following these steps:
Use
FoldableNavHostFragment
instead ofNavHostFragment
in the fragment container view by changing<androidx.fragment.app.FragmentContainerView android:id="@+id/nav_host_fragment" android:name="androidx.navigation.NavHostFragment"
to
<androidx.fragment.app.FragmentContainerView android:id="@+id/nav_host_fragment" android:name="androidx.navigation.FoldableNavHostFragment"
Use
findFoldableNavController
in order to get the instance forFoldableNavController
and use it to navigate inside navigation graph by changingfindNavController().navigate(R.id.action_next)
to
findFoldableNavController().navigate(R.id.action_next)
Use
FoldableNavigationUI
instead ofNavigationUI
, by changingval navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment val navController = navHostFragment.navController val appBarConfiguration = AppBarConfiguration(navController.graph) setupActionBarWithNavController(navController, appBarConfiguration)
to
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as FoldableNavHostFragment val navController = navHostFragment.navController val appBarConfiguration = FoldableAppBarConfiguration(navController.graph) setupActionBarWithFoldableNavController(navController, appBarConfiguration)