Navigation Architecture Component - Dialog Fragments
May 2019 Update:
DialogFragment are now fully supported starting from
Navigation 2.1.0
, you can read more here and here
Old Answer for Navigation <= 2.1.0-alpha02:
I proceeded in this way:
1) Update Navigation
library at least to version 2.1.0-alpha01
and copy both files of this modified gist in your project.
2) Then in your navigation host fragment, change the name
parameter to your custom NavHostFragment
<fragment android:id="@+id/nav_host_fragment" android:name="com.example.app.navigation.MyNavHostFragment" app:defaultNavHost="true" app:navGraph="@navigation/nav_graph" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/toolbar" />
3) Create your DialogFragment
subclasses and add them to your nav_graph.xml
with:
<dialog android:id="@+id/my_dialog" android:name="com.example.ui.MyDialogFragment" tools:layout="@layout/my_dialog" />
4) Now launch them from fragments or activity with
findNavController().navigate(R.id.my_dialog)
or similar methods.
No, as of the 1.0.0-alpha01
build, there is no support for dialogs as part of your Navigation Graph. You should just continue to use show() to show a DialogFragment
.
Yes. The framework is made in such a way that you can create a class extending the Navigator
abstract class for the views that does not come out-of-the box and add it to your NavController
with the method getNavigatorProvider().addNavigator(Navigator navigator)
If you are using the NavHostFragment
, you will also need to extend it to add the custom Navigator or just create your own MyFragment
implementing NavHost
interface. It's so flexible that you can create your own xml parameters with custom attrs defined in values
, like you do creating custom views. Something like this (not tested):
@Navigator.Name("dialog-fragment")class DialogFragmentNavigator( val context: Context, private val fragmentManager: FragmentManager) : Navigator<DialogFragmentNavigator.Destination>() { override fun navigate(destination: Destination, args: Bundle?, navOptions: NavOptions?, navigatorExtras: Extras? ): NavDestination { val fragment = Class.forName(destination.name).newInstance() as DialogFragment fragment.show(fragmentManager, destination.id.toString()) return destination } override fun createDestination(): Destination = Destination(this) override fun popBackStack() = fragmentManager.popBackStackImmediate() class Destination(navigator: DialogFragmentNavigator) : NavDestination(navigator) { // The value of <dialog-fragment app:name="com.example.MyFragmentDialog"/> lateinit var name: String override fun onInflate(context: Context, attrs: AttributeSet) { super.onInflate(context, attrs) val a = context.resources.obtainAttributes( attrs, R.styleable.FragmentNavigator ) name = a.getString(R.styleable.FragmentNavigator_android_name) ?: throw RuntimeException("Error while inflating XML. " + "`name` attribute is required") a.recycle() } }}
Usage
my_navigation.xml
<?xml version="1.0" encoding="utf-8"?><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/navigation" app:startDestination="@id/navigation_home"> <fragment android:id="@+id/navigation_assistant" android:name="com.example.ui.HomeFragment" tools:layout="@layout/home"> <action android:id="@+id/action_nav_to_dialog" app:destination="@id/navigation_dialog" /> </fragment> <dialog-fragment android:id="@+id/navigation_dialog" android:name="com.example.ui.MyDialogFragment" tools:layout="@layout/my_dialog" /></navigation>
The fragment that will navigate.
class HomeFragment : Fragment(), NavHost { private val navControllerInternal: NavController by lazy(LazyThreadSafetyMode.NONE){ NavController(context!!) } override fun getNavController(): NavController = navControllerInternal override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Built-in navigator for `fragment` XML tag navControllerInternal.navigatorProvider.addNavigator( FragmentNavigator(context!!, childFragmentManager, this.id) ) // Your custom navigator for `dialog-fragment` XML tag navControllerInternal.navigatorProvider.addNavigator( DialogFragmentNavigator(context!!, childFragmentManager) ) navControllerInternal.setGraph(R.navigation.my_navigation) } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { super.onCreateView(inflater, container, savedInstanceState) val view = inflater.inflate(R.layout.home) view.id = this.id view.button.setOnClickListener{ getNavController().navigate(R.id.action_nav_to_dialog) } return view }}