A multi back stack android navigation

Overview

Labyrinth

A multi back stack android navigation

MIT License - Copyright (c) 2020 Abanoub Milad Nassief Hanna
[email protected]
@Linkedin
@Github

Screen shots

How to add

Add to project level build.gradle

allprojects {
    repositories {

        maven { url "https://jitpack.io" }
        
    }
}

Add to app level build.gradle

    dependencies {

        implementation 'com.github.abanoubmilad:labyrinth:0.4'
        
    }

How to use

class ExampleMultiNavActivity : AppCompatActivity(), INavHolder {

    lateinit var labyrinth: Labyrinth
    override fun getINav() = labyrinth

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.example_multi_nav_activity)

        labyrinth = Builder(
            viewModelStoreOwner = this,
            lifecycleOwner = this,
            fragmentManager = supportFragmentManager,

        /*
        *   @ResId of FragmentContainerView that exists in your layout
        *   type: androidx.fragment.app.FragmentContainerView 
        */
            fragmentContainerId = R.id.nav_host_container, 
            
        /*
        *   BottomNavigationView that exists in your layout
        *   type: com.google.android.material.bottomnavigation.BottomNavigationView
        */
            bottomNavigationView = bottom_nav,

        /*
        *   list of lazy fragment initializer where each initializer 
        *   represents the back stack root fragment
        */
            rootTabFragmentsInitializer = listOf(
                { Welcome() },
                { Leaderboard() },
                { Register() }
            ),
            
        /*
        *   map of each menu item id to it's corresponding stack index
        *   if a menu item should not map to a stack do not pass it
        *
        *   if a menu ResId maps to a stack index, once clicked by user
        *   the module will navigate to the corresponding stack and display it
        *   onNavTabSelected callback will be fired
        * 
        *   if a menu ResId does not map to a stack index, once clicked by user
        *   onNonNavTabSelected callback will be fired
        */
            menuItemIdToRootTabFragmentIndexMap = hashMapOf(
                R.id.home to 0,
                R.id.list to 1,
                R.id.form to 2

            )).apply {
         
        /*
        *   OPTIONAL CUSTOMIZATIONS
        */
        
        
        /*
        *   initial selected tab
        */
            defaultSelectedTabIndex  = 0
    
        /*
        *   if enabled, clicking same tab wil reset the current stack to its root fragment
        *   else, clicking same tab will behave as clicking back i.e pop current stack
        */
            resetOnSameTabClickEnabled = true
        
        /*
        *   if enabled, the fragments will survive any configuration change
        *   i.e fragments will be maintained even if activity is recreated
        *   this functionality is achieve using a view model
        */
            saveStateEnabled  = false
        
        /*
        *   fragments pushed into current tab
        *   will be retained as long as their current tab is the selected one (active tab)
        * 
        *   when tab is switched you can choose to keep the non active stack fragments in memory
        *   so that when you switch back you will get the same instances
        *   OR destroy the non active stack fragments and recreate them (with same bundle) when their tab is selected again
        * 
        *   if enabled, non active tab (any tab that is not selected) fragments will be retained in memory
        *   if disabled, non active tab fragments will be destroyed and recreated 
        *   with their previous bundle when the tab is selected again
        *   
        *   For better performance it's suggested to keep this disabled (false)
        *   so fragments of other tab can be destroyed and release memory resources
        * 
        */
            retainNonActiveTabFragmentsEnabled = false
    
        /*
        *   if enabled, tabs history will be maintained
        */
            tabHistoryEnabled = true
    
        /*
        *   onNavTabSelected: ((menItemId: Int) -> Unit)?
        *   a callback fired when a bottom menu tab is clicked (nav tab)
        *   i.e when a bottom menu tab that corresponds to a back stack is clicked
        *   
        *   a bottom menu tab corresponds to a stack if 
        *   its exists in the menuItemIdToRootTabFragmentIndexMap
        * 
        */
            onNavTabSelected = null
         
        /*
        *   onNonNavTabSelected: ((menItemId: Int) -> Unit)?
        *   a callback fired when a bottom menu tab is clicked (non nav tab)
        *   i.e when a bottom menu tab that does not correspond to a back stack is clicked
        *   
        *   a bottom menu tab does not correspond to a stack if 
        *   its id does exist in the menuItemIdToRootTabFragmentIndexMap
        * 
        */
            onNonNavTabSelected = null

        }.build()
    }


    override fun onDestroy() {
        /*
        *   labyrinth should release it's resources
        */
        labyrinth.onDestroy()
        super.onDestroy()
    }

    override fun onBackPressed() {
        /*
        *   if shouldCallSuperOnBackPressed is true it means that labyrinth
        *   can not move any more back i.e both back stack and tabs history are empty
        */
        if (labyrinth.shouldCallSuperOnBackPressed())
            super.onBackPressed()
    }

}
 /*
  *
  *  Note: using a fragment that does not extend NavFragment will also work
  *  but then you wont have the onVisible() callback since the fragment view wont be retained
  *  i.e it will behave same as setting shouldSaveState = false
  *  where the view will be created each time
  *  (but of course you will still have the multi stack behaviour as expected)
  * 
  */
class About1 : NavFragment() {   
    /*
    *   open val shouldSaveState = false
    * 
    *   if set to true:
    * 
    *       the fragment will retain its view 
    *       (leak canary will complain about this as a leak, but it's a false positive one)
    *       i.e the view will survive back stack operations unless the fragment itself is dismissed
    *   
    *       in this case:
    *           
    *           buildRootView() is guaranteed to be called once only
    *           onCreated() is guaranteed to be called once only
    *           this callback is suitable for doing your view initializations
    * 
    *           onVisible() called is guaranteed to be called at least once
    *           as it will be called each time the fragment becomes visible again on ui
    *           if you use view model, you need to observe the view model inside the
    *           the onVisible() not the onCreated()
    * 
    *      example:      
    *      fragment A is added to backs tack -> A.buildRootView() ->  A.onCreated()->  A.onVisible() -> fragment B 
    *      is added on top of A -> .... fragment B is dismissed -> A.onVisible() -> .......
    *  
    *   if set to false:
    * 
    *       the fragment will not retain its view
    *       i.e the view will be destroyed during the back stack operations
    *   
    *       in this case:
    * 
    *           buildRootView(), onCreated() and onVisible() 
    *           will be called in order, each time the fragment becomes visible on ui
    *           whether it's the first time for this fragment or it's already restored from the back stack
    *           
    *           buildRootView() should be used to inflate the view as usual
    *           onCreated() should be used for view initializations
    *           onVisible() becomes useless in this case since it's always called with onCreated()
    *           but it can be used for view model observations this is helpful for
    *           better code separations and in case you decide to switch shouldSaveState to true
    *           as shouldSaveState = true requires view model observation to be done in onVisible()
    * 
    * 
    *      example:      
    *      fragment A is added to backs tack -> A.buildRootView() ->  A.onCreated()->  A.onVisible() -> fragment B 
    *      is added on top of A -> .... fragment B is dismissed -> A.buildRootView() ->  A.onCreated()->  A.onVisible() .......
    * 
    * 
    */
        override val shouldSaveState = true
        
    /*
    *   must override to specify how to build the fragment view
    *   this is really useful if you gonna have custom view initializations
    *   like data binding for example.
    * 
    *   example:
    * 
    *       override fun buildRootView(inflater: LayoutInflater, container: ViewGroup?): View {
    *            val binding: ProfileFragmentBinding =
    *                   DataBindingUtil.inflate(inflater, layoutId, container, false)
    *            binding.viewModel = viewModel
    *            binding.lifecycleOwner = this
    *            return binding.root
    *       } 
    *   
    */
        override fun buildRootView(
            inflater: LayoutInflater,
            container: ViewGroup?
        ): View {
            return inflater.inflate(R.layout.fragment_about1, container, false)
        }

    
        override fun onCreated() {
        }
    
    
        override fun onVisible() {
        }

}
class ExampleSingleNavActivity : AppCompatActivity(), INavHolder {

    lateinit var labyrinth: LabyrinthSingle
    override fun getINav() = labyrinth

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.example_single_nav_activity)

        labyrinth = BuilderSingle(
            viewModelStoreOwner = this,
            lifecycleOwner = this,
            fragmentManager = supportFragmentManager,

            fragmentContainerId = R.id.nav_host_container
        ).apply {

            saveStateEnabled = true
        /*   
        *
        *   if enabled, bottom stack fragments will be retained in memory
        *   if disabled, bottom stack fragments will be destroyed and recreated 
        *   with their previous bundle when they become at top of stack again
        * 
        *   For better performance it's suggested to keep this disabled (false)
        *   so fragments of bottom stack can be destroyed and release memory resources
        * 
        */
            retainNonActiveFragmentsEnabled = false

        }.build()

        labyrinth.navigate(About1())
    }


    override fun onBackPressed() {
        if (labyrinth.shouldCallSuperOnBackPressed())
            super.onBackPressed()
    }
}
You might also like...
🎉 [Android Library] A light-weight library to easily make beautiful Navigation Bar with ton of 🎨 customization option.
🎉 [Android Library] A light-weight library to easily make beautiful Navigation Bar with ton of 🎨 customization option.

Bubble Navigation 🎉 A light-weight library to easily make beautiful Navigation Bars with a ton of 🎨 customization options. Demos FloatingTopBarActiv

An android navigation bar widget
An android navigation bar widget

Chip Navigation Bar A navigation bar widget inspired on Google Bottom Navigation mixed with Chips component. Usage !-- bottom_menu.xml -- menu xmln

A small navigation library for Android to ease the use of fragment transactions & handling backstack (also available for Jetpack Compose).
A small navigation library for Android to ease the use of fragment transactions & handling backstack (also available for Jetpack Compose).

A small navigation library for Android to ease the use of fragment transactions & handling backstack (also available for Jetpack Compose).

A simple & curved & material bottom navigation for Android written in Kotlin with ♥ .
A simple & curved & material bottom navigation for Android written in Kotlin with ♥ .

A simple & curved & material bottom navigation for Android written in Kotlin with ♥ .

A simple navigation library for Android 🗺️

Enro 🗺️ A simple navigation library for Android "The novices’ eyes followed the wriggling path up from the well as it swept a great meandering arc ar

React Native lets you customize the navigation bar for Android.
React Native lets you customize the navigation bar for Android.

react-native-system-navigation-bar React Native lets you customize the navigation bar for Android. Hide Lean Back Immersive Sticky Immersive Low Profi

NavigationComponent-SendingData - Android Navigation Component : Pass value (arguments) in Fragments
NavigationComponent-SendingData - Android Navigation Component : Pass value (arguments) in Fragments

NavigationComponent-SendingData Android Navigation Component : Pass value (argum

AndroidBriefActions - Android library for sending and observing non persistent actions such as showing a message; nice readable way to call navigation actions from ViewModel or Activity/Fragment.

implementation "com.vladmarkovic.briefactions:briefactions:$briefActionsVersion" Benefits Why use brief-actions library pattern: Prevent short-term ac

NavigationSafeArgs - Passing data with Navigation Component in Android
NavigationSafeArgs - Passing data with Navigation Component in Android

Navigation SafeArgs Sample Passing data with Navigation Component in Android. Pr

Comments
  • about the back button

    about the back button

    how do I set the back button .

    I want to show the back button when at the next screen, so user can click the back button and go back to previous fragment.

    opened by Jackyaung 0
Owner
Abanoub Milad Nassief Hanna
Software Engineer
Abanoub Milad Nassief Hanna
[ACTIVE] Simple Stack, a backstack library / navigation framework for simpler navigation and state management (for fragments, views, or whatevers).

Simple Stack Why do I want this? To make navigation to another screen as simple as backstack.goTo(SomeScreen()), and going back as simple as backstack

Gabor Varadi 1.3k Jan 2, 2023
Android multi-module navigation built on top of Jetpack Navigation Compose

MultiNavCompose Android library for multi-module navigation built on top of Jetpack Navigation Compose. The goal of this library is to simplify the se

Jeziel Lago 21 Dec 10, 2022
Annotation processor that provides better navigation on android multi-modules projects 🛳.

FlowNav FlowNav is a mobile library for Android that helps and provider a better way to make multi-modules navigation. The main purpose of this librar

Jeziel Lago 134 Nov 16, 2022
Alligator is a modern Android navigation library that will help to organize your navigation code in clean and testable way.

Alligator Alligator is a modern Android navigation library that will help to organize your navigation code in clean and testable way. Features Any app

Artur Artikov 290 Dec 9, 2022
DSC Moi University session on using Navigation components to simplify creating navigation flow in our apps to use best practices recommended by the Google Android Team

Navigation Components Navigate between destination using safe args How to use the navigation graph and editor How send data between destinations Demo

Breens Mbaka 6 Feb 3, 2022
Bottom-App-Bar-with-Bottom-Navigation-in-Jetpack-compose-Android - Bottom App Bar with Bottom Navigation in Jetpack compose

Bottom-App-Bar-with-Bottom-Navigation-in-Jetpack-compose-Android This is simple

Shruti Patel 1 Jul 11, 2022
Navigation Component: THE BEST WAY to create navigation flows for your app

LIVE #017 - Navigation Component: A MELHOR FORMA de criar fluxos de navegação para o seu app! Código fonte do projeto criado na live #017, ensinando c

Kaique Ocanha 4 Jun 15, 2022
New style for app design simple bottom navigation with side navigation drawer UI made in Jetpack Compose.😉😎

BottomNavWithSideDrawer New style for app design simple bottom navigtaion with side navigation drawer UI made in Jetpack Compose. ?? ?? (Navigation Co

Arvind Meshram 5 Nov 24, 2022
Navigation Drawer Bottom Navigation View

LIVE #019 - Toolbar, Navigation Drawer e BottomNavigationView com Navigation Com

Kaique Ocanha 6 Jun 15, 2022
Animated Tab Bar is an awesome navigation extension that you can use to add cool, animated and fully customizable tab navigation in your apps

Animated Tab Bar is an awesome navigation extension that you can use to add cool, animated and fully customizable tab navigation in your apps. The extension provides handy methods and properties to change the behaviour as well as the appearance of the navigation bar.

Zain Ul Hassan 4 Nov 30, 2022