I'm building a chat application using Stream SDK. I'm using a BindingFrament from which all fragments can inherit and get initialize the binding for each fragment in the bindingFragment class using Generics. But it give nullPointerException.
package com.example.streamchat.ui
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.viewbinding.ViewBinding
import com.example.streamchat.databinding.FragmentLoginBinding
abstract class BindingFragment<out T : ViewBinding> : Fragment() {
private var _binding: ViewBinding? = null
@Suppress("UNCHECKED_CAST")
protected val binding: T
get() = _binding as T
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
_binding = bindingInflater(inflater)
return _binding!!.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
protected abstract val bindingInflater: (LayoutInflater) -> ViewBinding
}
Login Fragment :
package com.example.streamchat.ui.login
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewDebug.FlagToString
import android.view.ViewGroup
import android.widget.Toast
import androidx.core.view.isVisible
import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.viewbinding.ViewBinding
import com.example.streamchat.R
import com.example.streamchat.databinding.FragmentLoginBinding
import com.example.streamchat.ui.BindingFragment
import com.example.streamchat.utils.Constants
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import java.security.cert.TrustAnchor
@AndroidEntryPoint
class LoginFragment : BindingFragment<FragmentLoginBinding>() {
override val bindingInflater: (LayoutInflater) -> ViewBinding
get() = FragmentLoginBinding::inflate
private val viewModel : LoginViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding.btnConfirm.setOnClickListener{
setUpConnectingUIState()
viewModel.connectUser(binding.etUsername.text.toString())
}
binding.etUsername.addTextChangedListener {
binding.etUsername.error = null
}
subscribeToEvents()
return binding.root
}
private fun setUpConnectingUIState(){
binding.progressBar.isVisible = true
binding.btnConfirm.isVisible = false
}
private fun setUpIdealUIState(){
binding.progressBar.isVisible = false
binding.btnConfirm.isVisible = true
}
private fun subscribeToEvents(){
lifecycleScope.launch {
viewModel.logInEvent.collect{event ->
when(event){
is LoginViewModel.LogInEvent.ErrorInputTooShort->{
setUpIdealUIState()
binding.etUsername.error =
getString(R.string.error_username_too_short,Constants.MIN_USERNAME_LENGTH)
}
is LoginViewModel.LogInEvent.ErrorLogIn->{
setUpIdealUIState()
Toast.makeText(
requireContext(),
event.error,
Toast.LENGTH_LONG
).show()
}
is LoginViewModel.LogInEvent.Success->{
setUpIdealUIState()
}
}
}
}
}
}
But when I run the code it gives me this error.
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.streamchat, PID: 6243
java.lang.NullPointerException: null cannot be cast to non-null type T of com.example.streamchat.ui.BindingFragment
at com.example.streamchat.ui.BindingFragment.getBinding(BindingFragment.kt:17)
at com.example.streamchat.ui.login.LoginFragment.onCreateView(LoginFragment.kt:37)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:3104)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:524)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:261)
at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:113)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1433)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2977)
at androidx.fragment.app.FragmentManager.dispatchViewCreated(FragmentManager.java:2888)
at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:3129)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:552)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:261)
at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:113)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1433)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2977)
at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:2895)
at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:263)
at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:351)
at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:251)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1435)
at android.app.Activity.performStart(Activity.java:8018)
at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3475)
at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221)
at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
As LoginFragment is inheriting from BindingFragment and both these functions are overriding onCreateView(). So, in the LoginFragment we need to call
super.onCreateView(inflater, container, savedInstanceState)
So that the code we wrote in BindingFragment can run and initialize the binding variable.
Binding Fragment:
abstract class BindingFragment<T : ViewBinding> : Fragment() {
private var _binding: T? = null
lateinit var binding : T
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
_binding = bindingInflater(inflater, container)
binding = _binding!!
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
abstract fun bindingInflater(inflater: LayoutInflater, container: ViewGroup?): T
}
LoginFragment:
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
super.onCreateView(inflater, container, savedInstanceState)
}