1

So I setup my BottomNavigationView (like here) using Navigation Component library and everything works fine, every tab can keep their back stacks. However, if I add a Splash screen (Fragment) and:

  1. Set it as start destination (popUpInclusive set to true already)
  2. Create action from SplashFragment to the first tab HomeFragment

then all the tabs no longer keep their back stacks, plus the navigation becomes weird:

Splash -> Home (first tab) -> Me (second tab) -> Home -> press back, it goes back to Me instead of exiting the app.

PS: I'm using single Activity pattern with single navigation graph.

enter image description here

Sam Chen
  • 7,597
  • 2
  • 40
  • 73
  • Can you please add your navigation graph code here? – Android Geek Feb 24 '22 at 04:27
  • @Android Geek I've added the graph, do you still need the code? – Sam Chen Feb 24 '22 at 04:38
  • 3
    You should never be using login or splash screens as the start destination of your graph as per the [Principles of Navigation](https://developer.android.com/guide/navigation/navigation-principles#fixed_start_destination). There's a whole API [specifically for implement splash screens correctly](https://developer.android.com/guide/topics/ui/splash-screen). Is there a reason you aren't using that? – ianhanniballake Feb 24 '22 at 04:51

1 Answers1

1

Ok, thanks to @ianhanniballake, I post my final solution here, the key point is that the BottomNavigationView must be the start destination insead of other conditional destination like login or splash screen.


Step 1. Create and Setup Splash Layout and Fragment

  • Add SplashFragment into Navigation graph.

  • No need to create action from HomeFragment -> SplashFragment, unless you need transition animation

Step 2. Setup MainViewModel (shared ViewModel)

class MainViewModel : ViewModel() {
    private var _isFirstLaunch = true  //replace with the real condition in the future

    val isFirstLaunch: Boolean         //will be accessed by SplashFragment and HomeFragment
        get() = _isFirstLaunch

    fun updateIsFirstLaunch(isFirstLaunch: Boolean) {
        _isFirstLaunch = isFirstLaunch
    }
}

Step 3. Setup HomeFragment

class HomeFragment : Fragment() {
    private lateinit var binding: FragmentHomeBinding
    private lateinit var mainViewModel: MainViewModel  //shared ViewModel

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
        binding = DataBindingUtil.inflate(inflater, R.layout.fragment_home, container, false)
        mainViewModel = ViewModelProvider(requireActivity()).get(MainViewModel::class.java)

        if (mainViewModel.isFirstLaunch) {
            findNavController().navigate(R.id.splashFragment)   //no need action, unless you want transition animation
        }

        binding.goButton.setOnClickListener {
            findNavController().navigate(R.id.action_homeFragment_to_home2Fragment)
        }

        return binding.root
    }
}

Step 4. Setup SplashFragment

class SplashFragment : Fragment() {
    private lateinit var binding: FragmentSplashBinding
    private lateinit var mainViewModel: MainViewModel  //shared ViewModel

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
        binding = DataBindingUtil.inflate(inflater, R.layout.fragment_splash, container, false)
        mainViewModel = ViewModelProvider(requireActivity()).get(MainViewModel::class.java)

        binding.exitSplashButton.setOnClickListener {
            mainViewModel.updateIsFirstLaunch(false)  //update the condition

            findNavController().navigateUp()    //go back to HomeFragment
        } 

        requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
            requireActivity().finish()
        }

        return binding.root
    }
}

Demo: https://youtu.be/AxiIsY6BtGg

Sam Chen
  • 7,597
  • 2
  • 40
  • 73