androidkotlinandroid-fragmentsnavigationfragment-backstack

Issue with backstack and bottomnav in kotlin


I have a bottom nav with 4 fragments Home, Following, Notification, and Profile, there is no issue with the bottom navigation on backstack , but now for eg from profile fragment I jumped to a fragment called edit_profile which is not a part of the bottom nav and when press back I want that it should go back to the profile fragment but the backstack is taking me from edit_profile to directly home fragment

here is a recording link

I recently change my project from java to kotlin and I'm a beginner in kotlin

i really like the navigation of Pinterest and Instagram

Note:- All this code is automatically changed to kotlin (with some changes done manually ) , this issue was also with java and not after migrating to kotlin , Also if you want more reference of the code please tell me i will update the question

Code

MainActivity.kt // Bottom Nav

class MainActivity : AppCompatActivity() {
    var bottomNavigationView: BottomNavigationView? = null
    var integerDeque: Deque<Int> = ArrayDeque(3)
    var flag = true

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
        setContentView(R.layout.activity_main)
        val window = this.window
        window.statusBarColor = this.resources.getColor(R.color.black)
        bottomNavigationView = findViewById(R.id.bottom_navigation_view)
        integerDeque.push(R.id.nav_home)
        loadFragments(Home_Fragment())
        bottomNavigationView!!.selectedItemId = R.id.nav_home
        bottomNavigationView!!.setOnNavigationItemSelectedListener(
            BottomNavigationView.OnNavigationItemSelectedListener { item: MenuItem ->
                val id = item.itemId
                if (integerDeque.contains(id)) {
                    if (id == R.id.nav_home) {
                        integerDeque.size
                        if (flag) {
                            integerDeque.addFirst(R.id.nav_home)
                            flag = false
                        }
                    }
                    integerDeque.remove(id)
                }
                integerDeque.push(id)
                loadFragments(getFragment(item.itemId))
                false
            }
        )
    }

    @SuppressLint("NonConstantResourceId")
    private fun getFragment(itemId: Int): Fragment {
        when (itemId) {
            R.id.nav_home -> {
                bottomNavigationView!!.menu.getItem(0).isChecked = true
                return Home_Fragment()
            }
            R.id.nav_following -> {
                bottomNavigationView!!.menu.getItem(1).isChecked = true
                return Following_Fragment()
            }
            R.id.nav_notification -> {
                bottomNavigationView!!.menu.getItem(2).isChecked = true
                return Notification_Fragment()
            }
            R.id.nav_profile -> {
                bottomNavigationView!!.menu.getItem(3).isChecked = true
                return Profile_Fragment()
            }
        }
        bottomNavigationView!!.menu.getItem(0).isChecked = true
        return Home_Fragment()
    }

    private fun loadFragments(fragment: Fragment?) {
        if (fragment != null) {
            supportFragmentManager.beginTransaction()
                .replace(R.id.fragment_container, fragment, fragment.javaClass.simpleName)
                .commit()
        }
    }

    override fun onBackPressed() {
        integerDeque.pop()
        if (!integerDeque.isEmpty()) {
            loadFragments(getFragment(integerDeque.peek()))
        } else {
            finish()
        }
    }

Edit_Profile.kt // from this fragment i want to go back to the last fragment which should be the profile fragment

class Edit_Profile : Fragment() {
    private var profilePhoto: CircleImageView? = null
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_edit_profile, container, false)
        profilePhoto = view.findViewById(R.id.circleImageView)
        initImageLoader()
        setProfileImage()
        val imageView = view.findViewById<ImageView>(R.id.backArrow)
        imageView.setOnClickListener {
            val newCase: Fragment = Profile_Fragment()
            assert(fragmentManager != null)
            val transaction = requireFragmentManager().beginTransaction()
            transaction.replace(R.id.fragment_container, newCase)
            transaction.addToBackStack(Profile_Fragment.toString())
            transaction.commit()
        }
        return view
    }

Edit

added a part of the transaction from Profile Fragment to Edit Profile

ProfileFragment.kt

editProfileButton!!.setOnClickListener(View.OnClickListener { v: View? ->
            val edit_profile: Fragment = Edit_Profile()
            requireActivity().getSupportFragmentManager()
                .beginTransaction()
                .add(R.id.fragment_container, edit_profile,"TAG")
                .addToBackStack("TAG")
                .commit()
        })

Solution

  • Now you are managing the back stack through the integerDeque array.

    But since you pushed all those ids in the bottomNavigationView.setOnItemSelectedListener callback; then the integerDeque array only contains BottomNavigationView fragments ids.

    And as the Edit_Profile fragment is not a part of BottomNavigationView fragments, then it won't be added/popped off the queue. Instead when you try to popup the back stack whenever the Edit_Profile fragment is shown; the normal behavior you manage in the onBackPressed() continues and the Profile_Fragment id will pop up from the queue making you return to the preceding fragment (Home_Fragment) in your mentioned example.

    A little fix to this is to consider adding an id into the queue when you transact to Edit_Profile fragment so that this id is popped off the queue resulting in back to Profile_Fragment fragment.

    You can do that with the fragment's id in order to make sure it's unique:

    editProfileButton!!.setOnClickListener(View.OnClickListener { v: View? ->
            val edit_profile: Fragment = Edit_Profile()
            requireActivity().getSupportFragmentManager()
                .beginTransaction()
                .add(R.id.fragment_container, edit_profile,"TAG")
                .addToBackStack("TAG")
                .commit()
                
            (requireActivity() as MainActivity).integerDeque.push(id) // <<<< pushing id to the queue
            
        })
    

    This should fix your problem.


    Side tips: