Question

Android Data Binding in Kotlin: Fix Unresolved Reference Errors

kotlinjavaandroidandroid-databinding

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 root View for 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:

  1. Data Binding is enabled in the Android module.
  2. The layout is a Data Binding layout, using <layout> as its outer element.
  3. The module has built successfully so generated sources exist.
  4. 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.
  • FragmentDataBinding is 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.

Take Quiz

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:

  1. Gradle sees dataBinding = true.
  2. It processes fragment_data.xml, which has a <layout> root.
  3. It generates FragmentDataBinding during the build.
  4. onCreateView runs when Android needs the Fragment's view.
  5. FragmentDataBinding.inflate(...) creates the XML view hierarchy and returns a binding object.
  6. binding.titleText accesses the TextView with ID titleText.
  7. binding.root returns 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 findViewById calls.
  • Form screen: Connect input fields and validation messages to a form state object.
  • RecyclerView item: Generate an item binding such as ItemProductBinding for each row layout.
  • Loading and error states: Bind a view model's isLoading and errorMessage values to progress and error views.
  • Feature modules: Each module can generate bindings for the layouts it owns, while keeping view references type-safe.
Take Quiz

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 LiveData or Flow with viewLifecycleOwner, not the Fragment lifecycle, when updating views.
  • Clear _binding in onDestroyView to avoid retaining destroyed views.
  • Prefer BindingClass.inflate(...) for known layouts; use DataBindingUtil when 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 fileGenerated class
fragment_data.xmlFragmentDataBinding
item_user.xmlItemUserBinding
activity_main.xmlActivityMainBinding

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

ApproachWhat it doesBest fit
Data BindingGenerates a binding class and can bind layout expressions to dataXML-driven UIs that need binding expressions or lifecycle-aware observable data
View BindingGenerates a binding class for view IDs onlyMost screens that only need safe view references
findViewByIdFinds a view at runtime by IDLegacy code or unusual dynamic view lookup
Kotlin synthetic propertiesFormerly exposed views directly by IDDo 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.

Take Quiz

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.xml usually becomes FragmentDataBinding.
  • 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.

Take Quiz

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

  1. Enable Data Binding in the Android module.
  2. Create a fragment_profile.xml layout with a <layout> root.
  3. Add a TextView with the ID nameText.
  4. Inflate FragmentProfileBinding in a Fragment.
  5. Set the display name from Kotlin.
  6. Clear the binding in onDestroyView.
Take Quiz

Keep learning

Related questions

View more