I'm getting error "Can't access ViewModels from detached fragment" when onClick method implemented by AirportOnClickListener try do do anything with viewmodel (trigger event, set var etc). What's wrong with my code? I'm using hilt for DI
#AndroidEntryPoint
class SearchFragment #Inject constructor() : Fragment(), AirportOnClickListener {
#Inject
lateinit var adapter: RecyclerViewAdapter
private var _binding: FragmentAirportSearchBinding? = null
private val binding get() = _binding!!
private val airportSearchViewModel by viewModels<AirportSearchViewModel>()
private val args: SearchFragmentArgs by navArgs()
// recyclerview row click cause error "java.lang.IllegalStateException: Can't access ViewModels from detached fragment"
// when doing anything with airportSearchViewModel
override fun onClick(airport: Airport) {
airportSearchViewModel.setAirportTye(AirportType.Origin) //ERROR
airportSearchViewModel.triggerNavigation() //ERROR
Log.d("Selected airport", airport.name) //WORKS
}
class RecyclerViewAdapter #Inject constructor(private val airportOnClickListener: Provider<AirportOnClickListener>) :
RecyclerView.Adapter<SearchViewHolder>() {
var list = emptyList<Airport>()
set(value) {
field = value
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchViewHolder {
val binding = RowAirportSearchBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return SearchViewHolder(binding, airportOnClickListener.get())
}
override fun getItemCount(): Int {
return list.size
}
override fun onBindViewHolder(holder: SearchViewHolder, position: Int) {
holder.bind(list[position])
}
}
class SearchViewHolder(
private val binding: RowAirportSearchBinding,
private val airportOnClickListener: AirportOnClickListener,
) :
RecyclerView.ViewHolder(binding.root) {
fun bind(airport: Airport) {
with(binding) {
city.text = airport.city.name
country.text = airport.country.name
code.text = airport.code
root.setOnClickListener {
airportOnClickListener.onClick(airport)
}
}
}
}
#Module
#InstallIn(FragmentComponent::class)
abstract class AirportListenerModule {
#Binds
abstract fun bindClickListener(
searchFragment: SearchFragment,
): AirportOnClickListener
}
kotlin.UninitializedPropertyAccessException: lateinit property airportSearchViewModel has not been initialized
at com.jurgielewiczp.androidchallenge.views.search.SearchFragment.getAirportSearchViewModel(SearchFragment.kt:37)
at com.jurgielewiczp.androidchallenge.views.search.SearchFragment.onClick(SearchFragment.kt:44)
at com.jurgielewiczp.androidchallenge.views.search.SearchViewHolder.bind$lambda-1$lambda-0(RecyclerViewAdapter.kt:49)
at com.jurgielewiczp.androidchallenge.views.search.SearchViewHolder.$r8$lambda$dVFh158DngIJuL9pfu4VOonhCvg(Unknown Source:0)
at com.jurgielewiczp.androidchallenge.views.search.SearchViewHolder$$ExternalSyntheticLambda0.onClick(Unknown Source:4)
at android.view.View.performClick(View.java:6614)
at android.view.View.performClickInternal(View.java:6587)
at android.view.View.access$3100(View.java:787)
at android.view.View$PerformClick.run(View.java:26122)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:201)
at android.app.ActivityThread.main(ActivityThread.java:6831)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:927)
Related
I got some categories from an api and trying to show them on a recycler view but it doesn't work for some reason.
Although the data appears correctly in the logcat, it is sent as null to the Category adapter.
This is the Main Activity (where I'm trying to show the data):
`
#AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val TAG = "MEALZ"
private lateinit var binding: ActivityMainBinding
private val viewModel:MealsViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val adapter = CategoryAdapter(this)
binding.categoriesRv.adapter = adapter
viewModel.getMeals()
lifecycleScope.launch {
viewModel.categories.collect {
adapter.setData(it?.categories as List<Category>)
Log.d(TAG, "onCreate: ${it?.categories}")
}
}
}
}
`
This is Recycler Category Adapter :
`
class CategoryAdapter(private val context: Context?) :
RecyclerView.Adapter<CategoryAdapter.CategoryViewHolder>() {
private var categoryList: MutableList<Category?> = mutableListOf<Category?>()
inner class CategoryViewHolder(itemView: CategoryLayoutBinding) :
RecyclerView.ViewHolder(itemView.root) {
val name = itemView.categoryNameTv
val img = itemView.categoryIv
val des = itemView.categoryDesTv
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryViewHolder {
val binding = CategoryLayoutBinding.inflate(LayoutInflater.from(context), parent, false)
return CategoryViewHolder(binding)
}
override fun onBindViewHolder(holder: CategoryViewHolder, position: Int) {
var category = categoryList[position]
holder.name.text = category?.strCategory
holder.des.text = category?.strCategoryDescription
Glide.with(context as Context).load(category?.strCategoryThumb).into(holder.img)
}
override fun getItemCount(): Int {
return categoryList.size
}
fun setData(CategoryList: List<Category>) {
this.categoryList.addAll(CategoryList)
notifyDataSetChanged() //to notify adapter that new data change has been happened to adapt it
}
}
`
This is the View Model class:
#HiltViewModel
class MealsViewModel #Inject constructor(private val getMealsUseCase: GetMeals): ViewModel() {
private val TAG = "MealsViewModel"
private val _categories: MutableStateFlow<CategoryResponse?> = MutableStateFlow(null)
val categories: StateFlow<CategoryResponse?> = _categories
fun getMeals() = viewModelScope.launch {
try {
_categories.value = getMealsUseCase()
} catch (e: Exception) {
Log.d(TAG, "getMeals: ${e.message.toString()}")
}
}
}
you create your _categories with null as initial value, so first value of categories flow will be null and only second one will contain fetched data. As a workaround, you can check that data is not null:
viewModel.categories.collect {
if (it != null) {
adapter.setData(it?.categories as List<Category>)
Log.d(TAG, "onCreate: ${it?.categories}")
}
}
or introduce some kind of "loading" state
This is my main activity of my app. I'm using nav controller.
class MainActivity : AppCompatActivity() {
lateinit var viewModel: NewsViewModel
lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val newRepository = NewsRepository(ArticleDatabase(this))
val viewModelProviderFactory = NewsViewModelProviderFactory(newRepository)
viewModel = ViewModelProvider(this , viewModelProviderFactory).get(NewsViewModel::class.java)
// bottomNavigationView.setupWithNavController(newsNavHostFragment.findNavController())
val navHostFragment= supportFragmentManager.findFragmentById(R.id.newsNavHostFragment) as NavHostFragment
navController= navHostFragment.navController
bottomNavigationView.setupWithNavController(navController)
}
This the breaking news fragment. The function is same in other fragments so only posting this one
class BreakingNewsFragment : Fragment (R.layout.fragment_breaking_news) {
lateinit var viewModel: NewsViewModel
lateinit var newsAdapter: NewsAdapter
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = (activity as MainActivity).viewModel
setUpRecyclerView()
newsAdapter!!.setOnItemClickListener {
val bundle = Bundle().apply {
putSerializable("article" , it)
}
findNavController().navigate(
R.id.action_breakingNewsFragment_to_articleFragment2 , bundle
)
}
viewModel.breakingNews.observe(viewLifecycleOwner, Observer { response ->
when(response) {
is Resource.Success -> {
hideProgressBar()
response.data?.let { newsResponse ->
newsAdapter.differ.submitList(newsResponse.articles)
}
}
is Resource.Error -> {
hideProgressBar()
response.message?.let { message ->
Log.e("Breaking Fragment", "An error occured: $message")
}
}
is Resource.Loading -> {
showProgressBar()
}
}
})
}
private fun hideProgressBar() {
paginationProgressBar.visibility = View.INVISIBLE
}
private fun showProgressBar() {
paginationProgressBar.visibility = View.VISIBLE
}
fun setUpRecyclerView() {
newsAdapter = NewsAdapter()
rvBreakingNews.apply {
adapter = newsAdapter
layoutManager = LinearLayoutManager(activity)
}
}
}
This is made from Youtuber Philip lackner's tutorial. Here's the the link of original project
https://github.com/philipplackner/MVVMNewsApp . His version of fragment is deprecated so i used new fragment view in xml file. so there is an issue in using nav controller.
EDIT -> Forgot to put errors. Here it is
2022-10-17 01:08:09.796 3971-3971/com.arpit.newsapp20 E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.arpit.newsapp20, PID: 3971
java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.hashCode()' on a null object reference
at com.arpit.newsapp20.models.Article.hashCode(Unknown Source:15)
at androidx.navigation.NavBackStackEntry.hashCode(NavBackStackEntry.kt:256)
at java.util.HashMap.hash(HashMap.java:338)
at java.util.HashMap.put(HashMap.java:611)
at androidx.navigation.NavController.linkChildToParent(NavController.kt:143)
at androidx.navigation.NavController.addEntryToBackStack(NavController.kt:1918)
at androidx.navigation.NavController.addEntryToBackStack$default(NavController.kt:1813)
at androidx.navigation.NavController$navigate$4.invoke(NavController.kt:1721)
at androidx.navigation.NavController$navigate$4.invoke(NavController.kt:1719)
at androidx.navigation.NavController$NavControllerNavigatorState.push(NavController.kt:287)
at androidx.navigation.fragment.FragmentNavigator.navigate(FragmentNavigator.kt:198)
at androidx.navigation.fragment.FragmentNavigator.navigate(FragmentNavigator.kt:164)
at androidx.navigation.NavController.navigateInternal(NavController.kt:260)
at androidx.navigation.NavController.navigate(NavController.kt:1719)
at androidx.navigation.NavController.navigate(NavController.kt:1545)
at androidx.navigation.NavController.navigate(NavController.kt:1472)
at androidx.navigation.NavController.navigate(NavController.kt:1454)
at com.arpit.newsapp20.ui.BreakingNewsFragment$onViewCreated$1.invoke(BreakingNewsFragment.kt:31)
at com.arpit.newsapp20.ui.BreakingNewsFragment$onViewCreated$1.invoke(BreakingNewsFragment.kt:27)
at com.arpit.newsapp20.adapters.NewsAdapter.onBindViewHolder$lambda-2$lambda-1(NewsAdapter.kt:56)
at com.arpit.newsapp20.adapters.NewsAdapter.$r8$lambda$FmTlKYZBcoLQp02jU2NS9dL1z-k(Unknown Source:0)
at com.arpit.newsapp20.adapters.NewsAdapter$$ExternalSyntheticLambda0.onClick(Unknown Source:4)
at android.view.View.performClick(View.java:7441)
at android.view.View.performClickInternal(View.java:7418)
at android.view.View.access$3700(View.java:835)
at android.view.View$PerformClick.run(View.java:28676)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7839)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
Here is the article class
#Entity(
tableName = "articles"
)
data class Article(
#PrimaryKey(autoGenerate = true)
var id: Int? = null,
val author: String,
val content: String?,
val description: String,
val publishedAt: String,
val source: Source,
val title: String,
val url: String,
val urlToImage: String
) : Serializable
Here is the news adapter class
class NewsAdapter : RecyclerView.Adapter<NewsAdapter.ArticleViewHolder>() {
inner class ArticleViewHolder(itemView: View): RecyclerView.ViewHolder(itemView)
private val differCallback = object : DiffUtil.ItemCallback<Article>() {
override fun areItemsTheSame(oldItem: Article, newItem: Article): Boolean {
return oldItem.url == newItem.url
}
override fun areContentsTheSame(oldItem: Article, newItem: Article): Boolean {
return oldItem == newItem
}
}
val differ = AsyncListDiffer(this, differCallback)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArticleViewHolder {
return ArticleViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.item_article_preview,
parent,
false
)
)
}
override fun getItemCount(): Int {
return differ.currentList.size
}
private var onItemClickListener: ((Article) -> Unit)? = null
override fun onBindViewHolder(holder: ArticleViewHolder, position: Int) {
val article = differ.currentList[position]
holder.itemView.apply {
Glide.with(this).load(article.urlToImage).into(ivArticleImage)
tvSource.text = article.source.name
tvTitle.text = article.title
tvDescription.text = article.description
tvPublishedAt.text = article.publishedAt
setOnClickListener {
onItemClickListener?.let { it(article) }
}
}
}
fun setOnItemClickListener(listener: (Article) -> Unit) {
onItemClickListener = listener
}
}
You look like to want to pass the clicked news item to the article fragment. But in your click listener you don't receive the clicked item. Instead you pass something else in "it". I think that's why you get a null pointer exception.
You need something like following:
newsAdapter!!.setOnItemClickListener {
val newsItem = // get clicked news item from adapter
newsItem?.let {
val bundle = Bundle().apply {
putSerializable("article" , it)
}
findNavController().navigate(
R.id.action_breakingNewsFragment_to_articleFragment2 , bundle
)
}
}
I am trying to parse JSON (https://raw.githubusercontent.com/Biuni/PokemonGO-Pokedex/master/pokedex.json) to show data in RecyclerView, but I get an error:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.myapplication, PID: 13534
java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
at com.google.gson.stream.JsonReader.beginArray(JsonReader.java:350)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:80)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:61)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:39)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:27)
at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:243)
at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:153)
at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
I see that the problem is that JSON contains just 1 object (pokemon) and inside this object there is array of different pokemons. I don't know how to parse array inside the upper level object in JSON. What should I change to make it work?
I suppose that I should save this pokemon object and then parse it but I don't know hot to get inside it.
Thanks.
API interface:
interface SimpleApi {
#GET("pokedex.json")
suspend fun getCustomPosts(): Response<List<Post>>
}
Repository:
class Repository {
suspend fun getCustomPosts(): Response<List<Post>>{
return RetrofitInstance.api.getCustomPosts()
}
}
ViewModel:
class MainViewModel(val repository: Repository) : ViewModel() {
val myCustomPosts = MutableLiveData<Response<List<Post>>>()
fun getCustomPosts() {
viewModelScope.launch {
val response: Response<List<Post>> = repository.getCustomPosts()
myCustomPosts.value = response
}
}
}
MainActivity:
class MainActivity : AppCompatActivity() {
private lateinit var viewModel: MainViewModel
private lateinit var binding: ActivityMainBinding
private lateinit var recyclerView: RecyclerView
private lateinit var adapter: MyAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
setRecyclerView()
val repository = Repository()
val viewModelFactory = MainViewModelFactory(repository)
viewModel = ViewModelProvider(this, viewModelFactory).get(MainViewModel::class.java)
viewModel.getCustomPosts()
viewModel.myCustomPosts.observe(this, Observer { response ->
if (response.isSuccessful) {
response.body()?.let { adapter.setData(it) }
} else {
Toast.makeText(this, response.code(), Toast.LENGTH_SHORT).show()
}
})
}
private fun setRecyclerView() {
adapter = MyAdapter()
recyclerView = binding.recyclerView
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = adapter
}
}
RecyclerView Adapter:
class MyAdapter() : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
private var postList = emptyList<Post>()
class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val idView: TextView = view.findViewById(R.id.id_txt)
val nameView: TextView = view.findViewById(R.id.name_txt)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.row_layout, parent, false)
)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val currentItem = postList[position]
holder.idView.text = currentItem.id.toString()
holder.nameView.text = currentItem.name
}
override fun getItemCount(): Int = postList.size
fun setData(newList: List<Post>){
postList = newList
notifyDataSetChanged()
}
}
Post:
data class Post(
#SerializedName("id")
val id: Int,
#SerializedName("name")
val name: String
)
Try to use next class in response object:
data class PokedexResponse (
#SerializedName("pokemon")
val pokemons: List<Post>
)
interface SimpleApi {
#GET("pokedex.json")
suspend fun getCustomPosts(): Response<PokedexResponse>
}
My guess is that you missed to parse pokemon object:
{
"pokemon": [{ ... }]
}
I am getting an exception in the first line of the code below
viewModel.homeLiveData.observe(this, Observer { list ->
list?.let {
mList.addAll(list)
adapter.notifyDataSetChanged()
}
})
java.lang.ClassCastException: java.lang.Class cannot be cast to androidx.lifecycle.ViewModel
The Whole code is Below
what is wrong with the cast ? is there anything wrong of I am creating my ViewModel?
My BaseActivity
abstract class BaseActivity<V : ViewModel> : DaggerAppCompatActivity(), HasSupportFragmentInjector {
#Inject
lateinit var fragmentAndroidInjector: DispatchingAndroidInjector<Fragment>
#Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
#LayoutRes
abstract fun layoutRes(): Int
protected lateinit var viewModel : V
protected abstract fun getViewModel() : Class<V>
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val id = item.itemId
if (id == android.R.id.home)
onBackPressed()
return super.onOptionsItemSelected(item)
}
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(layoutRes())
viewModel = ViewModelProviders.of(this, viewModelFactory).get(getViewModel())
}
override fun supportFragmentInjector(): AndroidInjector<Fragment> = fragmentAndroidInjector
}
then My Activity
class MainActivity : BaseActivity<MainViewModel>() {
override fun layoutRes(): Int = R.layout.activity_main
override fun getViewModel(): Class<MainViewModel> = MainViewModel::class.java
private val mList = mutableListOf<Any>()
private lateinit var adapter: DataAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// setSupportActionBar(toolbar)
recyclerView.apply {
layoutManager = GridLayoutManager(applicationContext, 2)
adapter = DataAdapter(context, mList)
}
viewModel.homeLiveData.observe(this, Observer { list ->
list?.let {
mList.addAll(list)
adapter.notifyDataSetChanged()
}
})
viewModel.getHomeItems()
}
and this is my ViewModel
class MainViewModel #Inject constructor() : ViewModel() {
val homeLiveData: MutableLiveData<List<HomeScreenModel>> = MutableLiveData()
fun getHomeItems() {
Handler().post {
val homeModleList = listOf(
HomeScreenModel(R.drawable.ic_launcher_background, MyApplication.instance.getString(R.string.settings))
)
homeLiveData.setValue(homeModleList)
}
}
}
In my opinion, your viewModel property, which you try to access in viewModel.homeLiveData is shadowed by getViewModel() abstract function that you declare in BaseActivity. This is because Kotlin thinks that getXyZ() is a getter for the field xyZ and thus when you access viewModel, the compiler thinks you want to call getViewModel, which is of type Class<V>. I suggest renaming either the function or the property and it should work then.
Check your viewModel factory if you implemented it correctly
I am trying to write a base activity with code like this
abstract class BaseActivity<T : ViewDataBinding, V : BaseViewModel> : AppCompatActivity(), HasSupportFragmentInjector {
val NO_VIEW_MODEL_BINDING_VARIABLE = -1
private lateinit var mViewModel: V
private lateinit var mViewDataBinding: T
#Inject
lateinit var mViewModelFactory : ViewModelProvider.Factory
#Inject
lateinit var mFragmentInjector: DispatchingAndroidInjector<Fragment>
abstract fun getViewModelBindingVariable() : Int
#LayoutRes
abstract fun getLayoutId() : Int
fun getDataBinding() : T {
return mViewDataBinding
}
fun getViewModel() : V {
return mViewModel
}
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
performDataBinding()
provideViewModel()
}
override fun supportFragmentInjector(): AndroidInjector<Fragment> {
return mFragmentInjector
}
private fun performDataBinding() {
mViewDataBinding = DataBindingUtil.setContentView(this, getLayoutId())
if (getViewModelBindingVariable() != NO_VIEW_MODEL_BINDING_VARIABLE) {
setViewModelBindingVariable()
}
}
private fun setViewModelBindingVariable() {
mViewDataBinding.setVariable(getViewModelBindingVariable(), mViewModel)
mViewDataBinding.executePendingBindings()
}
private fun provideViewModel() {
val clazz: Class<V> = getViewModelClass(javaClass)
mViewModel = ViewModelProviders.of(this, mViewModelFactory).get(clazz)
}
private fun getViewModelClass(aClass: Class<*>): Class<V> {
val type = aClass.genericSuperclass
return if (type is ParameterizedType) {
type.actualTypeArguments[1] as Class<V>
} else {
getViewModelClass(aClass.superclass)
}
}
}
and I also using databinding here, the problem I encountered is whenever I tried to reference the viewmodel class in my xml and return the generated value of the viewmodel by using BR.viewModel in getViewModelBindingVariable it gave an error saying that
Caused by: kotlin.UninitializedPropertyAccessException: lateinit property mViewModel has not been initialized
at com.rahmat.app.newsapp.features.base.BaseActivity.setViewModelBindingVariable(BaseActivity.kt:70)
at com.rahmat.app.newsapp.features.base.BaseActivity.performDataBinding(BaseActivity.kt:65)
at com.rahmat.app.newsapp.features.base.BaseActivity.onCreate(BaseActivity.kt:53)
at com.rahmat.app.newsapp.features.main.MainActivity.onCreate(MainActivity.kt:31)
This error didn't happen if I return NO_VIEW_MODEL_BINDING_VARIABLE but this made me cannot reference the viewmodel class that I want in my xml. I think there is some problem in my provideViewModel() function but I still don't know why. Any help would be much appreciated. thank you