HomebaseWorks
Lesson 3 of 4

Fragments, the Navigation component and passing arguments

Manage fragment view lifecycle correctly, move between screens with NavController, and pass data without crashes.

EWritten by the HomebaseWorks editorial team · reviewed 26 Oct 2026

Most Android apps today are one activity with many fragments, switched by the Navigation component. Screenings ask what happens to a fragment's view on rotation, how to send an ID to the next screen, and how to get a result back. The answers follow from a few lifecycle facts.

Advertisement

The fragment view has its own lifecycle

A fragment can outlive its view. When it goes on the back stack, onDestroyView() runs but the fragment object stays. Any reference to the view, including a view binding, must be cleared there, and anything observing LiveData or Flow must use viewLifecycleOwner, not this.

class JobListFragment : Fragment(R.layout.fragment_job_list) {
    private var binding: FragmentJobListBinding? = null

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        binding = FragmentJobListBinding.bind(view)
        binding?.openButton?.setOnClickListener {
            val args = bundleOf("jobId" to 42)
            findNavController().navigate(R.id.action_list_to_detail, args)
        }
    }

    override fun onDestroyView() {
        binding = null
        super.onDestroyView()
    }
}

Passing the layout ID to the Fragment constructor removes the need to override onCreateView. The detail fragment reads the argument with requireArguments().getInt("jobId").

Navigating with a graph

The navigation graph XML lists destinations and the actions between them. navigate(actionId) moves forward and adds to the back stack; popBackStack() goes back one; navigate(actionId, args, navOptions { popUpTo(R.id.loginFragment) { inclusive = true } }) removes screens you should not return to, such as login after a successful sign-in. With the Safe Args plugin the call becomes typed and the argument is checked at compile time:

val action = JobListFragmentDirections.actionListToDetail(jobId = 42)
findNavController().navigate(action)

// in JobDetailFragment
private val args: JobDetailFragmentArgs by navArgs()
val id = args.jobId

Getting data back and sharing state

Two tools cover almost every case. For a one-off result, such as a picker returning a choice, use the Fragment Result API. For state several screens read, use a ViewModel scoped to the activity or to the navigation graph.

// picker fragment
setFragmentResult("pickLocation", bundleOf("city" to "Chennai"))
findNavController().popBackStack()

// receiving fragment, in onViewCreated
setFragmentResultListener("pickLocation") { _, bundle ->
    binding?.cityText?.text = bundle.getString("city")
}

// shared state
private val shared: FilterViewModel by activityViewModels()

Common mistakes and a practice habit

  • Observing with this as the lifecycle owner, which leaks and fires callbacks on a destroyed view.
  • Keeping the binding after onDestroyView, which holds the old view in memory.
  • Calling navigate twice from a double tap, crashing with an unknown action error. Check the current destination first or disable the button.
  • Using the activity's supportFragmentManager inside a fragment instead of childFragmentManager for nested fragments.

Practice habit: build a two-screen app with a list and a detail, pass an ID forward with Safe Args, return a chosen value with the Result API, then rotate the device on each screen and confirm nothing is lost.

Advertisement
Continue with Android4 lessons · 1 interview set · 1 test · 1 certificate
Not tested

More in Web and app