Navigation Architecture Component - Dialog Fragments
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
}
}
}
Leonardo Deleon
💪 Team Calisthenics 🎵 Music lover 💻 Developer ✏️ Design enthusiast 🎤 Voice student
Updated on July 05, 2022Comments
-
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 about 6 yearsFeature 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 over 5 yearsIs there any update since the spring on the plan for dialogs?
-
ianhanniballake over 5 years@AdamHurwitz - feel free to star the existing issue for updates.
-
Allan Veloso over 5 yearsWhile the library is in alpha it is changing like crazy. This implementation does not work anymore in alpha11.
-
Julio E. Rodríguez Cabañas about 5 yearsI 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 almost 5 yearsI 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 almost 5 yearsHow to handle its Positive & negative click listener.
-
musooff almost 5 yearsAccepted answer is no longer valid as
DialogFragment
is now supported with2.1.0-alpha03
version -
MatPag about 4 years@Pinakin you handle results directly in your custom
DialogFragment
, for example using theonCreateDialog
and setting asetPositiveButton
andsetNegativeButton
listeners -
Rizki Oktavia Ningrum about 4 yearscan I implement that for TimePickerFragment?
-
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 over 3 yearsThis is the updated and correct solution! Works for me!
-
Andy over 3 yearsYou just need change your DialogFragment in NavGraph as Razor's answer below!
-
Hashir Labs almost 3 yearsDiaglog destinations are now supported and available in navigation ui component