Search code examples
androidkotlinandroid-jetpackandroid-navigationandroid-jetpack-navigation

Let DialogFragment in navigation not disappear on poping back stack


I have FragmentA, FragmentB and DialogFragment(BottomDialogFragment). I I abbreviated them as A,B and D

D will be shown after the button in A is clicked. It means A -> D

B will be shown after the button in D is clicked. It means D -> B

I config them in navigation.xml

<fragment
        android:id="@+id/A"
        android:name="com.example.A">

    <action
        android:id="@+id/A_D"
        app:destination="@id/D" />
</fragment>



<dialog
        android:id="@+id/D"
        android:name="com.example.D">

    <action
        android:id="@+id/D_B"
        app:destination="@id/B" />
</dialog>


<fragment
        android:id="@+id/B"
        android:name="com.example.B">
</fragment>

Now when I click the button in A, the fragment will jump to D.

Then I click the button in D, the fragment will jump to B.

But when I pop the navigation stack in B, it will back to A, and the D doesn't show.

What should I do? I want the D still exists on the surface of A.


Solution

  • What should I do? I want the D still exists on the surface of A.

    So far, this is not possible, because dialogs are handled in a separate window than the activities/fragments; and therefore their back stack is handled differently And this is because Dialog implements the FloatingWindow interface.

    Check this answer for more clarification.

    But to answer your question, there are two approaches in mind:

    1. Approach 1: Change fragment B to a DailogFragment, and in this case both B & D are dialogs and therefore when you popup the stack to back from B to D, you'll still see D showing.
    2. Approach 2: To have a flag that is set if you return from B to D, and when if so, you re-show D.

    Actually, approach 2 isn't that good, because it doesn't keep D in the back stack while you go from D to B; it's just a workaround; also the user would see the dialog transitioning/fade animation while it returns from B to D; so it's not natural at all. So, here only approach 1 will be discussed.

    Approach 1 in detail:

    Pros:

    • It's very natural and will keep the back stack the same as you would like to.

    Cons:

    • DialogFragment B has a limited window than the normal fragment/activity.
    • B is no longer a normal fragment, but a DialogFragment, so you might encounter some other limitations.

    To solve the limited window of B, you can use the below theme:

    <style name="DialogTheme" parent="Theme.MyApp">
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowFullscreen">false</item>
        <item name="android:windowIsFloating">false</item>
    </style>
    

    Where Theme.MyApp is your app's theme.

    And apply it to B using getTheme():

    class FragmentB : DialogFragment() {
    
        override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
            return layoutInflater.inflate(R.layout.fragment_b, container, false)
        }
    
        override fun getTheme(): Int = R.style.DialogTheme
        
    }
    

    Also you need to change B in the navigation graph to a dialog:

    <dialog
            android:id="@+id/B"
            android:name="com.example.B">
    </dialog>
    

    Preview: