I'm trying to make an app that can enter and store Bible Verses. I'm using Dagger Hilt to inject my Daos into my View Model . My App keeps crashing whenever I try to use viewModel to get my state and onEvent objects to pass to my "verseScreen() composable" in my MainActivity as shown below but I've looked at the code and everything seems fine but it keeps crashing, Error in Logcat is "Cannot create instance of viewModel ---> In this case versesViewModel" but shouldn't @AndroidEntryPoint take care of that or what I'm I missing or doing wrong?
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
private val viewModel: versesViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
SwordTheme {
Log.d("VIEW THIS","${viewModel.hashCode()}")
val state by viewModel.state.collectAsState()
Log.d("STATE HERE","${state.hashCode()}")
//verseScreen depends on states and onEvent to display dta
verseScreen(state = state, onEvents = viewModel::onEvent)
}
}
}
}
My ViewModel class:
@HiltViewModel
class versesViewModel @Inject constructor(
val thisRepository: dataBaseRepository
): ViewModel() {
// sortType is an enum containing 3 constants "byBOOK, byDATE, byTHEME"
private val _sortType = MutableStateFlow(sortType.byBOOK)
//SELECTING A DAO BASED ON THE SORTTYPE
private val _verses = _sortType.flatMapLatest { sorttype ->
when(sorttype){
sortType.byBOOK -> thisRepository.orderByBook()
sortType.byDATE -> thisRepository.orderByDate()
sortType.byTHEME -> thisRepository.orderByTheme()
}
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())
//verseState is a Data class to keep my states
private val _state = MutableStateFlow(verseState())
//COMBINING THE ABOVE FLOWS, To be used outside this View Model
val state = combine(_state,_sortType,_verses){ state, sortType, myverses ->
state.copy(
sortType = sortType,
verses = myverses
)
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), verseState())
//DEFINING THE EVENTS
//appEvents is a sealed interface containing all my possible Events
fun onEvent(event: appEvents){
when(event) {
is appEvents.deleteVerse -> {
viewModelScope.launch { thisRepository.deleteVerse(event.verse) }
}
is appEvents.enterVerse -> { _state.update { it.copy( verse = event.verse) } }
appEvents.hideDialog -> { _state.update { it.copy( isAddingVerse = false) } }
appEvents.saveVerse -> {
val verseNum = state.value.verseNum
val chapter = state.value.chapter
val bookNum = state.value.bookNum
val bookName = state.value.bookName
val verse = state.value.verse
val theme = state.value.theme
val date: Long = System.currentTimeMillis()
if (bookName.isBlank() || verse.isBlank())
return
val thisVerse = myVerses(
verseNum = verseNum,
bookName = bookName,
bookNum = bookNum,
theme = theme,
date = date,
verse = verse,
chapter = chapter)
viewModelScope.launch { thisRepository.addVerse(thisVerse) }
_state.update { it.copy(
isAddingVerse = false,
verse = "",
theme = myThemes.NO_THEME,
bookName = "",
chapter = "",
verseNum = "",
bookNum = ""
) }
}
is appEvents.setBookNum -> { _state.update {it.copy( bookNum = event.bookNum)} }
is appEvents.setBookName -> { _state.update {it.copy( bookName = event.bookName)} }
is appEvents.setChapter -> { _state.update {it.copy( chapter = event.chapter)} }
is appEvents.setVerseNum -> { _state.update {it.copy( verseNum = event.verseNum)} }
appEvents.showDialog -> { _state.update { it.copy( isAddingVerse = true) } }
is appEvents.sortVerses -> { _sortType.value = event.sortType}
is appEvents.enterTheme -> { _state.update { it.copy( theme = event.theme) } }
}
}
}
MY APP MODULE:
@Module
@InstallIn(SingletonComponent::class)
object appModule {
@Provides
@Singleton
fun mydatabase(appContext: myApp) =
Room.databaseBuilder(
appContext,
myDataBase::class.java,
"myVerses.DB"
).build()
@Singleton
@Provides
fun provideDao(db: myDataBase) = db.myDao()
}
MY DATABASE CLASS:
@Database( entities = [myVerses::class],
version = 1)
abstract class myDataBase:RoomDatabase() {
abstract fun myDao(): myFunctions //myFunctions is an Interface containing my Room Daos
}
MY DATABASE REPOSITORY CLASS:
class dataBaseRepository @Inject constructor(
val myfuns : myFunctions
) {
suspend fun addVerse(verse: myVerses) = myfuns.addVerse(verse)
suspend fun deleteVerse(verse: myVerses) = myfuns.deleteVerse(verse)
fun orderByBook() = myfuns.orderByBook()
fun orderByDate() = myfuns.orderByDate()
fun orderByTheme() = myfuns.orderByTheme()
}
LOGCAT ERRORS(SCROLL RIGHT):
FATAL EXCEPTION: main
Process: com.example.Sword, PID: 27103
java.lang.RuntimeException: Cannot create an instance of class com.example.Sword.versesViewModel
at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.kt:204)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:322)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:304)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:278)
at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.kt:128)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:187)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:153)
at androidx.lifecycle.ViewModelLazy.getValue(ViewModelLazy.kt:53)
at androidx.lifecycle.ViewModelLazy.getValue(ViewModelLazy.kt:35)
at com.example.Sword.MainActivity.getViewModel(MainActivity.kt:32)
at com.example.Sword.MainActivity.access$getViewModel(MainActivity.kt:29)
at com.example.Sword.MainActivity$onCreate$1$1.invoke(MainActivity.kt:44)
at com.example.Sword.MainActivity$onCreate$1$1.invoke(MainActivity.kt:38)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
at androidx.compose.material3.TextKt.ProvideTextStyle(Text.kt:261)
at androidx.compose.material3.MaterialThemeKt$MaterialTheme$1.invoke(MaterialTheme.kt:81)
at androidx.compose.material3.MaterialThemeKt$MaterialTheme$1.invoke(MaterialTheme.kt:80)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
at androidx.compose.material3.MaterialThemeKt.MaterialTheme(MaterialTheme.kt:73)
at com.example.Sword.ui.theme.ThemeKt.SwordTheme(Theme.kt:65)
at com.example.Sword.MainActivity$onCreate$1.invoke(MainActivity.kt:38)
at com.example.Sword.MainActivity$onCreate$1.invoke(MainActivity.kt:37)
DEPENDECIES:
//FOR ROOM
implementation "androidx.room:room-ktx:2.5.2"
kapt "androidx.room:room-compiler:2.5.2"
//Dagger Hilt
implementation 'com.google.dagger:dagger:2.48.1'
kapt 'com.google.dagger:dagger-compiler:2.48.1'
implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03"
kapt "androidx.hilt:hilt-compiler:1.0.0"
I started Learning about injection using Dagger Hilt , 2 days ago so I haven't tried a'lot of options just a few edits here and there but nothing Worked.
After a'lot of suffering and praying, Turns out it was an easy fix:
MY APPLICATION CLASS BEFORE FIX:
@HiltAndroidApp
class myApp: Application() {
}
AFTER FIX:
@Module
@InstallIn(SingletonComponent::class)
@HiltAndroidApp
class myApp: Application() {
}
/*Note! it must be registered in manifest (<application
name = ".myApp" ) */
USING myApp() CONTEXT TO BUILD ROOM IN HILT MODULE:
@Module
@InstallIn(SingletonComponent::class)
object appModule {
@Provides
@Singleton
fun mydatabase(@ApplicationContext context: Context) =
/* For some reason @ApplicationContext worked and not passing an instance of
myApp() e.g fun mydatabase(context: myApp) */
Room.databaseBuilder(
context,
myDataBase::class.java,
"myVerses.DB"
).build()
@Singleton
@Provides
fun provideDao(db: myDataBase) = db.myDao()
}
MY MAINACTIVITY:
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
private val viewModel: versesViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
SwordTheme {
Log.d("VIEW THIS","${viewModel.hashCode()}")
val state by viewModel.state.collectAsState()
Log.d("STATE HERE","${state.hashCode()}")
// A surface container using the 'background' color from the theme
verseScreen(state = state, onEvents = viewModel::onEvent)
}
}
}
}
IN build.gradle(app) :
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
}
dependencies {
// ViewModel Compose
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.4.1"
//FOR ROOM
implementation "androidx.room:room-ktx:2.5.2"
kapt "androidx.room:room-compiler:2.5.2"
//Dagger - Hilt
implementation "com.google.dagger:hilt-android:2.40.5"
kapt "com.google.dagger:hilt-android-compiler:2.40.5"
implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03"
kapt "androidx.hilt:hilt-compiler:1.0.0"
implementation 'androidx.hilt:hilt-navigation-compose:1.0.0'
//
}
hilt {
enableAggregatingTask = false
}
kapt {
correctErrorTypes true
}
IN build.gradle(Project) :
buildscript {
dependencies {
classpath "com.google.dagger:hilt-android-gradle-plugin:2.40.5"
}
}