Question
Fixing NullPointerException When Accessing Views in a Kotlin Fragment
Question
I am using Kotlin Android Extensions inside an Android Fragment, and I get a NullPointerException when I try to access a view inside onCreateView().
The crash says:
Caused by: java.lang.NullPointerException: Attempt to invoke virtual
method 'android.view.View android.view.View.findViewById(int)' on a
null object reference
Here is the fragment code:
package com.obaied.testrun.Fragment
import android.os.Bundle
import android.support.v4.app.Fragment
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.obaied.acaan.R
import kotlinx.android.synthetic.main.fragment_card_selector.*
class CardSelectorFragment : Fragment() {
val TAG = javaClass.canonicalName
companion object {
fun newInstance(): CardSelectorFragment {
return CardSelectorFragment()
}
}
override fun onCreateView(
inflater: LayoutInflater?,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val rootView = inflater?.inflate(R.layout.fragment_card_selector, container, false)
btn_K.setOnClickListener {
Log.d(TAG, "onViewCreated(): hello world")
}
return rootView
}
}
How should Kotlin Android Extensions be used correctly with Fragments, and why does this crash happen?
Short Answer
By the end of this page, you will understand why Fragment views can be null during the wrong lifecycle stage, how Kotlin Android Extensions behaved in Fragments, and how to safely access views using the Fragment's root view or modern View Binding.
Concept
In Android, a Fragment has two related but different lifecycles:
- the Fragment lifecycle itself
- the Fragment's View lifecycle
This distinction is the reason many beginners see NullPointerException when working with UI elements in Fragments.
When onCreateView() runs, you are responsible for creating and returning the Fragment's view hierarchy. Until that view is properly created and associated with the Fragment, direct access to synthetic view properties like btn_K may fail.
In your example, this line is the problem:
btn_K.setOnClickListener { ... }
The synthetic property tries to find btn_K from the Fragment's current root view. But inside onCreateView(), that root view is not yet fully attached as the Fragment's active view, so the lookup can happen against null, causing a crash.
This matters because Fragments are widely used in:
- multi-screen Android apps
- tab layouts
- navigation components
- reusable UI sections
If you access views at the wrong time, your app becomes fragile and crashes during screen creation, recreation, or navigation.
Also, Kotlin Android Extensions are now deprecated. In modern Android development, View Binding is the recommended approach because it is type-safe and works clearly with the Fragment view lifecycle.
Mental Model
Think of a Fragment like a manager of a room, and the Fragment's View as the furniture inside that room.
- The manager (
Fragment) may exist before the furniture is fully set up. onCreateView()is when you start assembling the furniture.- Only after the room is ready should you safely interact with a chair or table (
Button,TextView, etc.).
If you try to sit on a chair before it has been placed in the room, you get an error.
That is what happens when you access a view too early in a Fragment: the UI element is being requested before the Fragment has a valid view hierarchy to search in.
Syntax and Examples
Correct approach with the inflated root view
If you are using the older synthetic access pattern, access views from the inflated rootView inside onCreateView().
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val rootView = inflater.inflate(R.layout.fragment_card_selector, container, false)
rootView.btn_K.setOnClickListener {
Log.d(TAG, "hello world")
}
return rootView
}
Here, btn_K is resolved from rootView, not from the Fragment before its view is ready.
Better lifecycle-based approach: use onViewCreated()
A safer pattern is to inflate the layout in onCreateView() and work with child views in onViewCreated().
override fun onCreateView(
inflater: ,
container: ?,
savedInstanceState: ?
): View {
inflater.inflate(R.layout.fragment_card_selector, container, )
}
{
.onViewCreated(view, savedInstanceState)
btn_K.setOnClickListener {
Log.d(TAG, )
}
}
Step by Step Execution
Consider this code:
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val rootView = inflater.inflate(R.layout.fragment_card_selector, container, false)
return rootView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
btn_K.setOnClickListener {
Log.d("CardSelectorFragment", "clicked")
}
}
What happens step by step
onCreateView()is called.- The layout file
fragment_card_selector.xmlis inflated into a realViewobject. - That
Viewis returned to the Fragment system. - The Fragment now officially has a created view hierarchy.
onViewCreated()is called with thatview.btn_Kcan now be found safely because the Fragment's view exists.
Real World Use Cases
Fragments and safe view access appear in many real Android scenarios:
- Navigation screens: setting click listeners after a screen layout is created
- Tabbed interfaces: each tab often uses a Fragment with its own view lifecycle
- Form screens: accessing
EditText,Button, and validation messages safely - RecyclerView host screens: wiring adapters after the layout exists
- API result screens: updating labels, loading states, and retry buttons in
onViewCreated()
Example: a login Fragment often inflates the layout in onCreateView() and sets listeners in onViewCreated():
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.loginButton.setOnClickListener {
// validate input and submit
}
}
This keeps UI setup aligned with the view lifecycle.
Real Codebase Usage
In real Android projects, developers usually follow a few consistent patterns.
1. Inflate in onCreateView(), bind in onViewCreated()
This is the most common pattern because it separates:
- view creation from
- view setup
2. Use View Binding instead of synthetic properties
Synthetic properties were convenient, but they hid lifecycle details. View Binding makes the view reference explicit.
3. Clear view references in onDestroyView()
Fragments can outlive their views. If you keep references too long, you risk memory leaks.
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
4. Use guard clauses when binding may be unavailable
In async code, developers often avoid touching views if the Fragment view has already been destroyed.
if (_binding == null) return
binding.statusText.text = "Loaded"
5. Keep UI logic near lifecycle-safe callbacks
Typical places:
Common Mistakes
1. Accessing Fragment views too early
Broken example:
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val rootView = inflater?.inflate(R.layout.fragment_card_selector, container, false)
btn_K.setOnClickListener { }
return rootView
}
Why it fails:
btn_Kis being resolved from the Fragment before its view is fully ready.
Fix:
- use
rootView.btn_K, or - move setup to
onViewCreated()
2. Forgetting the .view.* synthetic import when using rootView
If you want to do this:
rootView.btn_K
you needed the correct import:
import kotlinx.android.synthetic.main.fragment_card_selector.view.*
Without it, the property is not available on .
Comparisons
| Approach | How it works | Pros | Cons |
|---|---|---|---|
| Kotlin synthetic properties in Fragment | Access views like btn_K directly | Short syntax | Deprecated, hides lifecycle issues |
rootView.findViewById() | Find views manually from inflated root | Explicit and safe in onCreateView() | More boilerplate |
rootView.btn_K with synthetic .view.* import | Access synthetic properties from the actual View | Cleaner than findViewById() | Still deprecated |
| View Binding | Generated binding class gives typed references | Recommended, type-safe, lifecycle-friendly |
Cheat Sheet
Fragment view access quick reference
- A Fragment is not the same thing as its
View - Inflate layout in
onCreateView() - Access child views safely in
onViewCreated() - Prefer View Binding in modern Android
- Clear Fragment binding in
onDestroyView()
Safe patterns
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return inflater.inflate(R.layout.fragment_card_selector, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
btn_K.setOnClickListener { }
}
val rootView = inflater.inflate(R.layout.fragment_card_selector, container, false)
rootView.findViewById<Button>(R.id.btn_K).setOnClickListener { }
return rootView
FAQ
Why does a Fragment get NullPointerException when accessing a button?
Because the Fragment's view hierarchy may not be ready yet. Accessing a view before the Fragment has a valid root view causes the lookup to fail.
Should I use onCreateView() or onViewCreated() for setting click listeners?
Usually onViewCreated() is better because the view has already been created and is safe to use.
Are Kotlin Android synthetic properties still recommended?
No. They are deprecated. Use View Binding in modern Android projects.
Why do Fragments need onDestroyView() cleanup?
A Fragment can remain alive after its view is destroyed. Clearing binding prevents memory leaks and invalid view access.
Can I use findViewById() in a Fragment?
Yes. You can call it on the inflated root view or on the view passed to onViewCreated().
What is the safest modern way to access views in a Fragment?
View Binding is the recommended approach because it is explicit, type-safe, and fits the Fragment view lifecycle.
Why did rootView.btn_K work but btn_K crash?
accesses the button from the actual inflated layout object. on the Fragment depends on the Fragment's own current view reference, which may not be ready yet.
Mini Project
Description
Build a simple Fragment that shows a button and a status message. When the user taps the button, the status text changes from Ready to Clicked. This project demonstrates the correct way to inflate a Fragment layout, access views safely, and clean up binding properly.
Goal
Create a Fragment that uses View Binding to safely interact with its views without causing lifecycle-related crashes.
Requirements
- Create a Fragment layout with a Button and a TextView.
- Inflate the layout using View Binding in
onCreateView(). - Set the button click listener in
onViewCreated(). - Update the TextView when the button is tapped.
- Clear the binding reference in
onDestroyView().
Keep learning
Related questions
Accessing Kotlin Extension Functions from Java
Learn how Kotlin extension functions are compiled and how to call them correctly from Java with clear examples and common pitfalls.
Android AlarmManager Example: Scheduling Tasks with AlarmManager
Learn how to use Android AlarmManager to schedule tasks, set alarms, and handle broadcasts with a simple beginner example.
Android Foreground Service Notification Channels in Kotlin
Learn why startForeground fails on Android 8.1 and how to create a valid notification channel for foreground services in Kotlin.