I was developing an app in kotlin, and I try to make a custom dialog, which extends of my parent class BaseDialogFragment, and when I try to instace my GenericDialog I get the following error:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: es.renaultbank.andr, PID: 21498
java.lang.IllegalStateException: Fragment GenericDialog{a0c2f1e} (ffc56cd1-3856-466f-8705-c66209c24963 tag=sucess_otp_dialog) did not return a View from onCreateView() or this was called before onCreateView().
at androidx.fragment.app.Fragment.requireView(Fragment.java:1964)
at es.renaultbank.andr.utils.GenericDialog.configureEvents(GenericDialog.kt:39)
at es.renaultbank.andr.ui.customviews.dialog.BaseDialogFragment.onCreateView(BaseDialogFragment.kt:17)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2963)
at androidx.fragment.app.DialogFragment.performCreateView(DialogFragment.java:489)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:518)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:282)
My GenericDialog class is the following:
package es.renaultbank.andr.utils
import android.app.Dialog
import android.content.Context
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import es.renaultbank.andr.R
import es.renaultbank.andr.ui.customviews.dialog.BaseDialogFragment
import es.renaultbank.andr.ui.extensions.find
class GenericDialog(
context: Context,
private val icon: Int? = null,
private val title: String? = null,
private val subtitle: String? = null,
private val buttonText: String? = null,
private var clickListener: View.OnClickListener? = null,
private val cancelable: Boolean = true
) : BaseDialogFragment() {
private var iv_icon: ImageView = ImageView(context)
private var tv_title: TextView = TextView(context)
private var tv_subtitle: TextView = TextView(context)
private var btn_action: Button = Button(context)
private var iv_close: ImageView = ImageView(context)
override fun configureView(view: View?) {
dialog?.setCancelable(true)
dialog?.setCanceledOnTouchOutside(true)
}
override fun configureEvents(view: View?) {
iv_icon = requireView().findViewById(R.id.iv_icon)
tv_title = requireView().findViewById(R.id.tv_title)
tv_subtitle = requireView().findViewById(R.id.tv_subtitle)
btn_action = requireView().findViewById(R.id.btn_action)
iv_close = requireView().findViewById(R.id.iv_close)
}
override fun getLayout(): Int {
return R.layout.dialog_generic
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setView()
setListeners()
}
fun setIcon(icon: Int) {
iv_icon.setImageDrawable(requireContext().getDrawable(icon))
iv_icon.visibility = View.VISIBLE
iv_icon.setColorFilter(R.color.black)
}
fun setTitle(title: String) {
tv_title.text = title
tv_title.visibility = View.VISIBLE
}
fun setSubtitle(subtitle: String) {
tv_subtitle.text = subtitle
tv_subtitle.visibility = View.VISIBLE
}
fun setButtonText(text: String) {
btn_action.text = text
btn_action.visibility = View.VISIBLE
}
fun setOnClickListener() {
btn_action.setOnClickListener { clickListener }
}
override fun setCancelable(flag: Boolean) {
super.setCancelable(flag)
if (flag) {
iv_close.visibility = View.VISIBLE
} else {
iv_close.visibility = View.GONE
}
}
private fun setView() {
if (icon != null) {
setIcon(icon)
} else {
iv_icon.visibility = View.GONE
}
if (title != null) {
setTitle(title)
} else {
tv_title.visibility = View.GONE
}
if (subtitle != null) {
setSubtitle(subtitle)
} else {
tv_subtitle.visibility = View.GONE
}
if (buttonText != null) {
setButtonText(buttonText)
} else {
btn_action.visibility = View.GONE
}
setCancelable(cancelable)
}
fun dismissDialog(){
this.dismiss()
}
private fun setListeners() {
iv_close.setOnClickListener {
dismiss()
}
if (clickListener != null) {
btn_action.setOnClickListener(clickListener)
} else {
btn_action.setOnClickListener {
dismiss()
}
}
}
}
and their parent class is this:
BaseDialogFragment.kt
package es.renaultbank.andr.ui.customviews.dialog
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import androidx.fragment.app.DialogFragment
abstract class BaseDialogFragment : DialogFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val rootView: View = inflater.inflate(getLayout(), container, false)
configureView(rootView)
configureEvents(rootView)
return rootView
}
fun hideKeyboard(view: View?) {
view?.let {
val imm = view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(view.windowToken, 0)
}
}
/**
* Configuracion de los findViewById la vista, colores, animaciones...
*/
protected abstract fun configureView(view: View?)
/**
* Configuracion de listeners
*/
protected abstract fun configureEvents(view: View?)
/**
* Setea el layout del dialog
*/
protected abstract fun getLayout(): Int
}
Finally the used of the GenericDialog is a simple network call, and if sucess, show the dialog
if(it.errorBase.getErrorType() == ErrorBase.ErrorType.NON_ERROR) {
(it.responseBase).let {resp ->
if ((resp as ValidatePassOTPResponse).response == "200 OK") {
val sucessDialog = GenericDialog(
context = requireContext(),
icon = R.drawable.ic_tick_green,
title = getString(R.string.change_password_title),
subtitle = getString(R.string.password_change_sucess),
buttonText = getString(R.string.understand),
cancelable = true,
clickListener = { (activity as DashboarActivity).redirectToLogin() }
)
sucessDialog.show(requireFragmentManager(), "sucess_otp_dialog")
} else {
showOtpError().also {
(activity as DashboarActivity).redirectToLogin()
}
}
}
}else{
(activity as BaseActivity<*>).showError(it.errorBase, null)
dialog?.dismiss().also {
(activity as DashboarActivity).redirectToLogin()
}
}
})
I hope you can help, in case you've faced some similar problem.
Take thanks in advance !
[EDIT]
I've taken to show the dialog replacing requireView to view, in the configureEvents method. but althought is shown and debugging I see that the set method in the dialog are invoked, the dialog show like that:
As you can check is empty although the params which I'm passing are not empty.
