| Independent Single Stacks | Top Level MultiStack Navigation | Nested MultiStack Navigation |
|---|---|---|
![]() |
![]() |
![]() |
An interface for changing navigation destinations with Fragments.
This class enforces rules about Fragments and the containers they are in a bid to make working with Fragments a lot easier.
It's fairly opinionated, but it allows for very predictable Fragment interactions.
The basic crux of it's operation is that every Fragment added to a container must be added to the Fragment back stack, with a deterministic tag.
This way, if during navigation, a Fragment is requested to be shown, and an identical instance of that Fragment exists,
that same instance is retrieved from the back stack and placed on top of the stack. The most convenient way to generate this tag,
is by implementing the TagProvider interface, and delegating the stableTag property to the Fragment's arguments.
It also helps in preventing the same Fragment instance being shown twice, one after the other.
If the same Fragment instance is asked to be shown, the request is simply ignored.
Another benefit is retrieving the current Fragment in the container, which is extremely useful for shared element transitions.
This is because it allows for the isolation of FragmentTransition logic from the action that causes or starts them.
If for example clicking a view causes a FragmentTransaction with a shared element transition, the building of the FragmentTransaction for the Transition need not occur
at the click site. In the case of an instance of TransactionModifier for example, a augmentTransaction(transaction: FragmentTransaction, incomingFragment: Fragment) API is exposed,
allowing for the Fragment leaving to customize the FragmentTransaction for the Fragment about to come in, yielding a nice separation of concerns.
Interfaces:
-
TransactionModifier: A delegate for customizing the FragmentTransaction for Fragments shown and popped of the stack. -
TagProvider: Typically implemented by the Fragment's themselves, this is an interface to provide a tag for each Fragment added to the stack.
It also provides:
-
A
transactionModifiersettable property reference to aTransactionModifierfor augmenting theFragmentTransactionthat shows each Fragment in the stack. -
A convenience
currentFragmentproperty for accessing the current Fragment on top of the stack.
A class that allows for independent stacks of navigation, each backed by a StackNavigator instance. It is extremely useful for integrating
multiple back stacks with a bottom navigation View. It does this using the child FragmentManager within instances of a StackFragment
It also provides:
-
A
stackSelectedListener((Int) -> Unit)?callback for when stack selection has changed, either directly or indirectly. -
A
stackTransactionModifier(FragmentTransaction.(Int) -> Unit)?for augmenting theFragmentTransactionthat switches between the rootStackFragmentfor each stack. -
A
transactionModifier(FragmentTransaction.(Fragment) -> Unit)?for augmenting theFragmentTransactionthat shows child fragment instances within eachStackFragment. This property is delegated internally to theStackNavigatorfor eachStackFragment.
class MainActivity : AppCompatActivity(R.layout.activity_main), StackNavigator.NavigationController {
private val multiStackNavigator: MultiStackNavigator by multiStackNavigationController(
R.id.content_container,
intArrayOf(R.id.menu_core, R.id.menu_recyclerview, R.id.menu_communications)
) { id -> RouteFragment.newInstance(id).let { it to it.stableTag } }
override val navigator: StackNavigator
get() = multiStackNavigator.currentNavigator
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
findViewById<BottomNavigationView>(R.id.bottom_navigation).apply {
multiStackNavigator.stackSelectedListener = { menu.findItem(it)?.isChecked = true }
multiStackNavigator.transactionModifier = { incomingFragment ->
val current = navigator.currentFragment
if (current is StackNavigator.TransactionModifier) current.augmentTransaction(this, incomingFragment)
else crossFade()
}
multiStackNavigator.stackTransactionModifier = { crossFade() }
setOnApplyWindowInsetsListener { _: View?, windowInsets: WindowInsets? -> windowInsets }
setOnNavigationItemSelectedListener { multiStackNavigator.show(it.itemId).let { true } }
setOnNavigationItemReselectedListener { multiStackNavigator.currentNavigator.clear() }
}
onBackPressedDispatcher.addCallback(this) { if (!multiStackNavigator.pop()) finish() }
}
}
Please look at MainActivity in the sample app for more detail.
Each Navigator also supports a performConsecutively method that takes a coroutine scope, which lets you perform
navigation operations one after the other as the FragmentManager is inherently asynchronous. This API is currently in alpha.


