Question
Android Data Binding in Kotlin: Fix Unresolved Reference Errors
Question
How can I use the Android Data Binding library from a Kotlin Fragment when the generated binding package and FragmentDataBinding class are reported as unresolved?
The equivalent Java fragment compiles successfully:
import com.example.app.databinding.FragmentDataBinding;
public class DataFragment extends Fragment {
private FragmentDataBinding binding;
@Override
public View onCreateView(
LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState
) {
binding = DataBindingUtil.inflate(
inflater,
R.layout.fragment_data,
container,
false
);
return binding.getRoot();
}
}
However, this Kotlin version reports Unresolved reference: databinding and Unresolved reference: FragmentDataBinding:
import com.example.app.databinding.FragmentDataBinding
class ProfileFragment : Fragment() {
private var binding: FragmentDataBinding? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = DataBindingUtil.inflate(
inflater,
R.layout.fragment_data,
container,
false
)
return binding!!.root
}
}
What Gradle setup and Kotlin code are required for Data Binding to generate and expose the binding class correctly?
Short Answer
Android Data Binding generates a strongly typed binding class from each layout that uses a <layout> root. In Kotlin, an unresolved binding class usually means Data Binding is not enabled, the layout does not qualify for generation, the generated class name or package is wrong, or Gradle has not completed a successful build. You will learn the modern Gradle configuration and a lifecycle-safe Fragment pattern.
Concept
Data Binding is an Android build feature that generates source code from XML layouts. It is not a class that you manually create.
For example, a layout file named fragment_data.xml normally generates a class named FragmentDataBinding:
fragment_data.xml -> FragmentDataBinding
That class exposes:
root, the rootViewfor the layout- References to views with IDs
- Variables declared in the layout, when used
Because the class is generated during the Android build, Kotlin can import it only after all of these conditions are true:
- Data Binding is enabled in the Android module.
- The layout is a Data Binding layout, using
<layout>as its outer element. - The module has built successfully so generated sources exist.
- The import matches the generated class name and package exactly.
Older Android Gradle Plugin versions used a separate com.android.databinding plugin and a dataBinder dependency. In current Android projects, enable Data Binding through buildFeatures; do not add the old standalone Data Binding plugin.
Mental Model
Think of a binding class as a receipt printed by a machine.
- Your XML layout is the order submitted to the machine.
- The Android Gradle Plugin is the machine.
FragmentDataBindingis the printed receipt.
If the machine is not switched on (Data Binding is disabled), if the order has the wrong format (the layout does not use <layout>), or if the machine failed midway through (the build failed), there is no receipt to pick up. Kotlin then correctly says that FragmentDataBinding does not exist.
Syntax and Examples
Enable Data Binding in the app or feature module that owns the layout.
// app/build.gradle.kts
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
}
android {
buildFeatures {
dataBinding = true
}
}
For Groovy Gradle files, use:
// app/build.gradle
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
android {
buildFeatures {
dataBinding true
}
}
A Data Binding layout must use <layout> as the outer tag:
<!-- res/layout/fragment_data.xml -->
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/titleText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Profile" />
</LinearLayout>
</layout>
Then Kotlin can use the generated class:
import com.example.app.databinding.FragmentDataBinding
val binding = FragmentDataBinding.inflate(inflater, container, false)
binding.titleText.text = "Welcome"
FragmentDataBinding.inflate(...) is a convenient generated method. It avoids explicitly calling DataBindingUtil.inflate(...) when the binding type is already known.
Step by Step Execution
Consider this layout file:
res/layout/fragment_data.xml
and this Kotlin code:
private var _binding: FragmentDataBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentDataBinding.inflate(inflater, container, false)
binding.titleText.text = "Loading profile"
return binding.root
}
Execution proceeds as follows:
- Gradle sees
dataBinding = true. - It processes
fragment_data.xml, which has a<layout>root. - It generates
FragmentDataBindingduring the build. onCreateViewruns when Android needs the Fragment's view.FragmentDataBinding.inflate(...)creates the XML view hierarchy and returns a binding object.binding.titleTextaccesses theTextViewwith IDtitleText.binding.rootreturns the top-level view, which the Fragment displays.
The binding is tied to the Fragment's view lifecycle, not necessarily the Fragment object's full lifecycle. Clear it in onDestroyView.
Real World Use Cases
Data Binding is useful when a layout needs a typed connection to its views or UI state.
- Profile screen: Set a name, avatar, and account status without repeated
findViewByIdcalls. - Form screen: Connect input fields and validation messages to a form state object.
- RecyclerView item: Generate an item binding such as
ItemProductBindingfor each row layout. - Loading and error states: Bind a view model's
isLoadinganderrorMessagevalues to progress and error views. - Feature modules: Each module can generate bindings for the layouts it owns, while keeping view references type-safe.
Real Codebase Usage
In modern Fragment code, developers generally avoid a nullable binding everywhere. Instead, they keep a nullable backing property and expose a non-null property that is used only between onCreateView and onDestroyView.
class ProfileFragment : Fragment() {
private var _binding: FragmentDataBinding? = null
private val binding: FragmentDataBinding
get() = _binding ?: error("Binding is only valid between onCreateView and onDestroyView")
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentDataBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.titleText.text = "Profile"
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
Common project patterns include:
- Inflate the binding in
onCreateView. - Set click listeners and render UI in
onViewCreated. - Observe
LiveDataorFlowwithviewLifecycleOwner, not the Fragment lifecycle, when updating views. - Clear
_bindinginonDestroyViewto avoid retaining destroyed views. - Prefer
BindingClass.inflate(...)for known layouts; useDataBindingUtilwhen the binding type or layout is selected dynamically.
Common Mistakes
1. Misspelling the generated class
Binding names are case-sensitive. This is incorrect:
import com.example.app.databinding.FragmentDataBdinding
It contains Bdinding instead of Binding. Use:
import com.example.app.databinding.FragmentDataBinding
2. Expecting a class from a non-Data-Binding layout
This does not generate FragmentDataBinding because its root is LinearLayout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" />
Wrap the layout in <layout> when using Data Binding.
3. Forgetting to enable the feature
Without this configuration, generated binding classes will be unavailable:
android {
buildFeatures {
dataBinding = true
}
}
4. Using the wrong generated name
The usual conversion is snake case to PascalCase plus Binding:
| Layout file | Generated class |
|---|---|
fragment_data.xml | FragmentDataBinding |
item_user.xml | ItemUserBinding |
activity_main.xml | ActivityMainBinding |
If a layout uses a custom class attribute on <data>, its generated name can differ.
5. Using !! to access a nullable binding
return mBinding!!.root
This works only while the value is non-null. It can crash if called after the view is destroyed or before initialization. Use the backing-property pattern and clear the binding in onDestroyView.
6. Keeping a binding after onDestroyView
A Fragment can remain alive after its view is removed. Holding its binding then retains the old view tree and can leak memory. Set _binding = null in onDestroyView.
7. Mixing obsolete and current Gradle setup
Do not add the historical com.android.databinding plugin to a modern Android Gradle Plugin project. Use buildFeatures { dataBinding = true } instead. After changing Gradle configuration, sync and run a clean build so generated sources are refreshed.
Comparisons
| Approach | What it does | Best fit |
|---|---|---|
| Data Binding | Generates a binding class and can bind layout expressions to data | XML-driven UIs that need binding expressions or lifecycle-aware observable data |
| View Binding | Generates a binding class for view IDs only | Most screens that only need safe view references |
findViewById | Finds a view at runtime by ID | Legacy code or unusual dynamic view lookup |
| Kotlin synthetic properties | Formerly exposed views directly by ID | Do not use in new projects; the feature is deprecated/removed |
Data Binding and View Binding both generate classes such as FragmentDataBinding. The important difference is that Data Binding requires a <layout> root and supports XML expressions, while View Binding does not require <layout> and is simpler when expressions are unnecessary.
For a known binding class, prefer:
FragmentDataBinding.inflate(inflater, container, false)
over the more generic form:
DataBindingUtil.inflate(inflater, R.layout.fragment_data, container, false)
Both can work, but the generated inflate method is shorter and keeps the type explicit.
Cheat Sheet
// Enable in build.gradle.kts
android {
buildFeatures {
dataBinding = true
}
}
<!-- A Data Binding layout must have this outer tag -->
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<!-- actual root view goes here -->
</layout>
// fragment_data.xml -> FragmentDataBinding
private var _binding: FragmentDataBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentDataBinding.inflate(inflater, container, false)
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
Rules:
- The generated class is case-sensitive.
- Build successfully after enabling Data Binding or changing layouts.
fragment_data.xmlusually becomesFragmentDataBinding.- Use the binding only while the Fragment view exists.
- Modern projects use
buildFeatures.dataBinding, not the old standalone Data Binding Gradle plugin.
FAQ
Why is FragmentDataBinding unresolved in Kotlin?
Usually Data Binding is disabled, the XML layout does not use a <layout> root, the class name is misspelled, or the project has not completed a successful Gradle build.
What class is generated for fragment_data.xml?
Normally, Android generates FragmentDataBinding. Layout words are converted from snake case to PascalCase and Binding is appended.
Do I need to import DataBindingUtil?
Not when you use the generated class's inflate method. Use FragmentDataBinding.inflate(...) for a known layout. DataBindingUtil remains useful for more dynamic cases.
Do I need the old com.android.databinding Gradle plugin?
No for current Android Gradle Plugin versions. Enable Data Binding with buildFeatures { dataBinding = true } in the Android module.
Why should a Fragment clear its binding in onDestroyView?
A Fragment can outlive its view. Clearing the binding prevents it from retaining references to a view hierarchy that Android has destroyed.
Can I use View Binding instead of Data Binding?
Yes. If you only need typed references to views and do not need XML binding expressions, View Binding is often the simpler choice.
Why does Java compile while Kotlin cannot import the binding class?
Generated-source visibility depends on the Android Gradle configuration and successful generation tasks. Ensure the module configuration is correct, sync Gradle, and fix any build errors. Also verify that the Kotlin import exactly matches the generated name.
Mini Project
Description
Create a small profile Fragment that inflates a Data Binding layout, shows a display name, and safely releases its view reference. This mirrors the setup used by real XML-based Android screens.
Goal
Build a Fragment that uses FragmentProfileBinding without unresolved references or leaked view references.
Requirements
- Enable Data Binding in the Android module.
- Create a
fragment_profile.xmllayout with a<layout>root. - Add a
TextViewwith the IDnameText. - Inflate
FragmentProfileBindingin a Fragment. - Set the display name from Kotlin.
- Clear the binding 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.
Add a Maven Repository URL in Gradle Kotlin DSL (build.gradle.kts)
Learn how to add Maven Central and a custom Maven repository URL in Gradle Kotlin DSL using build.gradle.kts syntax.
Allow HTTP and HTTPS in Android 9 Pie with Network Security Configuration
Learn how Android 9 Pie handles cleartext HTTP traffic and how to allow HTTP and HTTPS safely using network security config.