Skip to content

Quick Tutorials

Kaustubh Patange edited this page Oct 10, 2021 · 10 revisions

Table of contents

Initialize Navigator & navigate to start destination

// initialize Navigator & with start destination as MyFragment.
val navigator = FragmentNavigator.with(this, savedStateInstance)
    .initialize(findViewById(R.id.fragment_container), /*optional*/ Destination.of(MyFragment::class))

Furthermore, to navigate to other destination you can call navigator.navigateTo() or navigator.show() respectively.

Note: The host in which Navigator is set up must implement NavigationTransmittor interface to propogate navigator instance to its child fragments.

Navigate to a Fragment but remember the transaction

Why? Because Navigator does not add your fragment transaction to backstack. You must explicitly specify this, so that on pressing back button (or navigating up) would take you back to the previous fragment.

navigator.navigate(MyFragment::class, FragmentNavigator.NavOptions(remember = true))
class MyFragment : ValueFragment(...) {
    override fun onBackPressed() : Boolean {
        if (condition-true) {
            // do something to make condition false.
            return true
        }
        return super.onBackPressed() // this call will remove the fragment.
    }
}

Now when you call navigator.goBack() from the host or when back button is pressed (assuming your setup is correct), FragmentNavigator will first decide whether it is safe to remove this fragment from the backstack & then a call to onBackPressed() will be invoked & based on the results the remove operation is performed.

Navigation with typed arguments

  • Create an argument class (typically a data class).
@Parcelize
data class MyArgs(
    val name: String,
    val age: Int
    // any other types that can be parceled or serialized.
) : BaseArgs()
  • Create the args & navigate to the destination fragment.
val args = MyArgs(
    name = "John",
    age = 20
)

navigator.navigate(MyFragment::class, FragmentNavigator.NavOptions(args = args))
  • Parse the arguments in the destination fragment. The destination fragment must extend from ValueFragment.
class MyFragment : ValueFragment() {
    override fun onViewCreated(...) {
        ...
        if (hasKeyArgs<MyArgs>()) {
            val args = getKeyArgs<MyArgs>()
            println("Name: ${args.name}, Age: ${args.age}")

            // clearArgs() // (optional) to consume args.
        }
    }
}

Navigation with animation

  • There are some built-in animation as well as support for custom animation.
  • This includes the traditional anim transition to the new animator one as well as transition. However, if you don't want to create them, Navigator provides some default ones built on top of this.
  • Check CircularTransform custom animation to circular reveal fragments during a transaction.
// A simple fade transition.
val options = FragmentNavigator.NavOptions(
    animation = Animation.Fade // or SlideInRight, SlideInLeft, etc.
)

// Supported "anim", "animator", or "transition"
val options = FragmentNavigator.NavOptions(
    animation = Animation.Custom(
        destinationEntering = R.anim.slide_from_right,
        currentExiting = R.anim.fade
    )
)

/**
 * Shared element transition.
 * - In xml set android:transitionName on the view eg: cardView (Fragment A)
 * - (Fragment B) set android:transitionName on the view to share the transition eg: "heroCard".
 */
val options = FragmentNavigator.NavOptions(
    animation = Animation.Shared(
        elements = mapOf(
            binding.cardView to "heroCard"
        )
        // optional enter/exit transition (defaults to fade).
    )
)

// Navigate...
navigator.navigate(MyFragment::class, options)

Navigation with single top instance or popUpTo

  • If you've used Jetpack Navigation Component, there is a concept of single top instance or popUpTo when defining action. Basically, it helps you to clear backstack upto certain fragments.

  • In case of singleTopInstance only one instance of such fragment are allowed in backstack. If there are multiple instance of this fragment the previous one will be removed & the current one will be placed.

  • Clearing all previous backstack when navigating,

val options = FragmentNavigator.NavOptions(
    historyOptions = HistoryOptions.ClearHistory
)

navigator.navigateTo(MyFragment::class, options)
  • Maintain single instance of the fragment,
val options = FragmentNavigator.NavOptions(
    historyOptions = HistoryOptions.SingleTopInstance
)

navigator.navigateTo(MyFragment::class, options)
  • Clear history up to specific Fragment class or backstack name,
val options = FragmentNavigator.NavOptions(
    historyOptions = HistoryOptions.PopToFragment(PreviousFragment::class, all = false) // or PopToBackStack
)

navigator.navigateTo(MyFragment::class, options)

Navigate to a BottomSheet or Dialog Fragment

  • From the host
navigator.show(MyBottomSheetDialog::class, ...)
  • From the child fragment
getParentNavigator().show(MyBottomSheetDialog::class, ...) // this uses parent's FragmentManager.
// or
getSimpleNavigator().show(MyBottomSheetDialog::class, ...) // this uses child's FragmentManager (recommended).
class MyBottomSheetDialog : BottomSheetDialogFragment() {
    override fun onViewCreated(...) {
        ...
        // optional arguments you can pass when calling navigator.show()
        if (hasKeyArgs<MyArgs>()) {
            val args = getKeyArgs<MyArgs>()
            // process arguments..
        }
    }
}