Introduction: "I did everything by the tutorial, but the app crashes!"
\nIf you've just started your journey in Android development with Kotlin, you've probably already encountered this moment. You write code following a tutorial, launch the app... and crash. In the logs — the coveted java.lang.NullPointerException (or its Kotlin equivalent). And the message: "Cannot read field… because the view is null".
This is a real pain that has gathered thousands of views on Stack Overflow. Today we will analyze it once and for all. Why does a NullPointerException occur when accessing a View in a Fragment in Kotlin? And, most importantly, how to fix it so you can sleep peacefully.
\nLet's go!
\n\nWhy does the error occur? The root of the evil — Lifecycle
\nThe main reason beginners get a NullPointerException in Kotlin Fragment is a misunderstanding of the fragment lifecycle. In an Activity, everything is simple: onCreate — and go ahead. In a Fragment, everything is trickier.
A fragment goes through several stages:
\n- \n
onCreateView()— here the layout is inflated (created). \n onViewCreated()— here the View definitely already exists. \n onDestroyView()— the View is destroyed (for example, during a screen rotation). \n
The most popular error is trying to access the View before the fragment has created its layout. For example, in the constructor or in the fragment's onCreate(). At this moment, view equals null — hence the crash.
The second reason is a reference leak. You saved a reference to the View in a variable, and then the fragment was recreated, and the old reference leads nowhere.
\n\nTypical example: what the problem looks like in code
\nImagine you have a simple fragment with a text field:
\nclass MyFragment : Fragment() {\n\n private lateinit var myTextView: TextView\n\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n // ERROR! The view doesn't exist yet here\n myTextView = requireView().findViewById(R.id.my_text_view)\n myTextView.text = "Hello!"\n }\n\n override fun onCreateView(\n inflater: LayoutInflater,\n container: ViewGroup?,\n savedInstanceState: Bundle?\n ): View? {\n return inflater.inflate(R.layout.fragment_my, container, false)\n }\n}\nThis code will crash with a NullPointerException because requireView() in onCreate() returns null. The View hasn't been created yet!
3 working ways to fix the error
\nLet's look at three approaches that will guaranteed solve the problem of accessing a View in a Fragment.
\n\nMethod 1: Move the code to onViewCreated()
\nThe simplest and most correct way is to wait for the moment when the View definitely exists. The onViewCreated() method is called immediately after the layout is created.
class MyFragment : Fragment() {\n\n private lateinit var myTextView: TextView\n\n override fun onCreateView(\n inflater: LayoutInflater,\n container: ViewGroup?,\n savedInstanceState: Bundle?\n ): View? {\n return inflater.inflate(R.layout.fragment_my, container, false)\n }\n\n override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n super.onViewCreated(view, savedInstanceState)\n // Now view is definitely not null!\n myTextView = view.findViewById(R.id.my_text_view)\n myTextView.text = "It works!"\n }\n}\nPros: simple, clear, always works.
\nCons: you need to remember about lateinit var and not forget to nullify references in onDestroyView() (otherwise a memory leak is possible).