Home > Mobile >  Fragment not attached to a context error when calling function from XML with binding expression
Fragment not attached to a context error when calling function from XML with binding expression

Time:06-06

I am trying to call a function in my fragment via expression binding from my XML file in "android:onclick...", but it will not work. The error is that the fragment is not attached to a context.

It is the

MaterialAlertDialogBuilder(requireContext())

which gives me headache.

How do I give the context to the fragment?

I have seen similar questions regarding that topic, but none that helped me.

Any help is much appreciated.

ItemDetailFragment.kt:

class ItemDetailFragment : Fragment() {

private lateinit var item: Item

private val viewModel: InventoryViewModel by activityViewModels {
    InventoryViewModelFactory(
        (activity?.application as InventoryApplication).database.itemDao()
    )
}

private val navigationArgs: ItemDetailFragmentArgs by navArgs()

private var _binding: FragmentItemDetailBinding? = null
private val binding get() = _binding!!

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    _binding = FragmentItemDetailBinding.inflate(inflater, container, false)
    return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    val id = navigationArgs.itemId

    binding.viewModel = viewModel

    binding.fragment = ItemDetailFragment()
}

/**
 * Displays an alert dialog to get the user's confirmation before deleting the item.
 */
fun showConfirmationDialog() {
    MaterialAlertDialogBuilder(requireContext())
        .setTitle(getString(android.R.string.dialog_alert_title))
        .setMessage(getString(R.string.delete_question))
        .setCancelable(false)
        .setNegativeButton(getString(R.string.no)) { _, _ -> }
        .setPositiveButton(getString(R.string.yes)) { _, _ ->
            deleteItem()
        }
        .show()
}

/**
 * Called when fragment is destroyed.
 */
override fun onDestroyView() {
    super.onDestroyView()
    _binding = null
}

}

fragment_item_detail.kt:

<?xml version="1.0" encoding="utf-8"?><!--
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">

<data>

    <variable
        name="viewModel"
        type="com.example.inventory.InventoryViewModel" />

    <variable
        name="fragment"
        type="com.example.inventory.ItemDetailFragment" />

</data>

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="@dimen/margin"
    tools:context=".ItemDetailFragment">

    <Button
        android:id="@ id/delete_item"
        style="?attr/materialButtonOutlinedStyle"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/margin"
        android:onClick="@{()->fragment.showConfirmationDialog()}"
        android:text="@string/delete"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/sell_item" />

</androidx.constraintlayout.widget.ConstraintLayout>

That is the error i am getting:

java.lang.IllegalStateException: Fragment ItemDetailFragment{e562873} (c6ab2144-3bdc-410b-91eb-e5668e8b617a) not attached to a context.

CodePudding user response:

You should not pass your fragment instance as a data binding variable.
You could define a Boolean mutable live data variable in your InventoryViewModel and show the dialog when it changes:

private val _showConfirmation = MutableLiveData(false)
val showConfirmation
    get() = _showConfirmation

fun onShowConfirmation() {
    _showConfirmation.value = true
}

fun onConfirmationShown() {
    _showConfirmation.value = false
}

Then, define an observer for this property in your ItemDetailFragment:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    val id = navigationArgs.itemId

    binding.viewModel = viewModel
    binding.executePendingBindings()
    
    viewModel.showConfirmation.observe(viewLifecycleOwner) {
        if (it) {
            showConfirmationDialog()
            viewModel.onConfirmationShown()
        }
    }
}

Finally, remove the fragment variable from the XML and change your Button's onClick as:

<Button
    ...
    android:onClick="@{() -> viewModel.onShowConfirmation()}"
    />
  • Related