Navigation Architecture Component - Dialog Fragments

39,493

Solution 1

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.

Solution 2

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.

Solution 3

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
    }
}

Solution 4

Yes it is possible, You can access view of parent fragment from dialog fragment by calling getParentFragment().getView(). And use the view for navigation.

Here is the example

Navigation.findNavController(getParentFragment().getView()).navigate(R.id.nextfragment);

Solution 5

I created custom navigator for DialogFragment.

Sample is here.
(It's just sample, so it might be any problem.)

@Navigator.Name("dialog_fragment")
class DialogNavigator(
    private val fragmentManager: FragmentManager
) : Navigator<DialogNavigator.Destination>() {

    companion object {
        private const val TAG = "dialog"
    }

    override fun navigate(destination: Destination, args: Bundle?, 
            navOptions: NavOptions?, navigatorExtras: Extras?) {
        val fragment = destination.createFragment(args)
       fragment.setTargetFragment(fragmentManager.primaryNavigationFragment, 
               SimpleDialogArgs.fromBundle(args).requestCode)
        fragment.show(fragmentManager, TAG)
        dispatchOnNavigatorNavigated(destination.id, BACK_STACK_UNCHANGED)
    }

    override fun createDestination(): Destination {
        return Destination(this)
    }

    override fun popBackStack(): Boolean {
        return true
    }

    class Destination(
            navigator: Navigator<out NavDestination>
    ) : NavDestination(navigator) {

        private var fragmentClass: Class<out DialogFragment>? = null

        override fun onInflate(context: Context, attrs: AttributeSet) {
            super.onInflate(context, attrs)
            val a = context.resources.obtainAttributes(attrs,
                    R.styleable.FragmentNavigator)
            a.getString(R.styleable.FragmentNavigator_android_name)
                    ?.let { className ->
                fragmentClass = parseClassFromName(context, className, 
                        DialogFragment::class.java)
            }
            a.recycle()
        }

        fun createFragment(args: Bundle?): DialogFragment {
            val fragment = fragmentClass?.newInstance()
                ?: throw IllegalStateException("fragment class not set")
            args?.let {
                fragment.arguments = it
            }
            return fragment
        }
    }
}
Share:
39,493
Leonardo Deleon
Author by

Leonardo Deleon

💪 Team Calisthenics 🎵 Music lover 💻 Developer ✏️ Design enthusiast 🎤 Voice student

Updated on July 05, 2022

Comments

  • Leonardo Deleon
    Leonardo Deleon almost 2 years

    Is it possible to use the new Navigation Architecture Component with DialogFragment? Do I have to create a custom Navigator?

    I would love to use them with the new features in my navigation graph.

  • ianhanniballake
    ianhanniballake about 6 years
    Feature request for Navigation can be directed to the public issue tracker - make sure to state your use case and why you'd find dialogs in your Navigation Graph useful.
  • AdamHurwitz
    AdamHurwitz over 5 years
    Is there any update since the spring on the plan for dialogs?
  • ianhanniballake
    ianhanniballake over 5 years
    @AdamHurwitz - feel free to star the existing issue for updates.
  • Allan Veloso
    Allan Veloso over 5 years
    While the library is in alpha it is changing like crazy. This implementation does not work anymore in alpha11.
  • Julio E. Rodríguez Cabañas
    Julio E. Rodríguez Cabañas about 5 years
    I ran into this problem as well. Just a single test would have been enough to find this issue, yet they released a new version of the library that doesn't even work because they couldn't bother to test it. Makes you wonder what their quality process is like.
  • J.S.R - Silicornio
    J.S.R - Silicornio almost 5 years
    I have tried with last version and it is working but animations are not running for me. I have to set them in dialog class. Is this happening to someone too?
  • Pinakin Kansara
    Pinakin Kansara almost 5 years
    How to handle its Positive & negative click listener.
  • musooff
    musooff almost 5 years
    Accepted answer is no longer valid as DialogFragment is now supported with 2.1.0-alpha03 version
  • MatPag
    MatPag about 4 years
    @Pinakin you handle results directly in your custom DialogFragment, for example using the onCreateDialog and setting a setPositiveButton and setNegativeButton listeners
  • Rizki Oktavia Ningrum
    Rizki Oktavia Ningrum about 4 years
    can I implement that for TimePickerFragment?
  • Victor Rendina
    Victor Rendina almost 4 years
    @Pinakin if you are using navigation to go to a dialog fragment, think of it like navigating to any other fragment. How would you get a result back from navigating to a regular fragment? developer.android.com/training/basics/fragments/… If the dialog is "owned" by the fragment that started it and you want click listeners you may be better off using a Dialog and skipping navigation.
  • Andy
    Andy over 3 years
    This is the updated and correct solution! Works for me!
  • Andy
    Andy over 3 years
    You just need change your DialogFragment in NavGraph as Razor's answer below!
  • Hashir Labs
    Hashir Labs almost 3 years
    Diaglog destinations are now supported and available in navigation ui component