I am currently working with Jetpack compose, I have Koin for Dependency Injection in my Project.
When I inject viewModel into @Composable
function, and I observe some data from the Viewmodel, But the issue here is I can't preview my UI, It says render problem.
java.lang.IllegalStateException: KoinApplication has not been started
at org.koin.core.context.GlobalContext.get(GlobalContext.kt:36)
at org.koin.compose.KoinApplicationKt.getKoinContext(KoinApplication.kt:52)
at org.koin.compose.KoinApplicationKt.access$getKoinContext(KoinApplication.kt:1)
at org.koin.compose.KoinApplicationKt.getKoinScope(KoinApplication.kt:86)
It throws the above exception in issue panel in Preview screen.
As it says KoinApplication not started, But I have already Initialize my Koin in Application class and defined it in Manifest.
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
androidLogger()
androidContext(this@MainApplication)
modules(ApiModule)
modules(appModule)
}
}
}
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.ComposeRetrofit"
android:name=".MainApplication"
tools:targetApi="31">
And this is my compose code,
@Preview
@Composable
fun ViewForMainScreen(){
MainScreen()
}
@Composable
fun getApplianceViewModel(): ApplianceViewModel {
return koinViewModel()
}
@Composable
fun getAddressViewModel(): AddressViewModel {
return koinViewModel()
}
@Composable
fun MainScreen(){
val addressViewModel = getAddressViewModel()
val address by addressViewModel.addressApiResponse.observeAsState(initial = null)
Column(
modifier = Modifier
.fillMaxWidth()
.height(100.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Button(
onClick = { addressViewModel.callAddressApi() },
modifier = Modifier
.fillMaxWidth()
.height(40.dp)
.padding(start = 40.dp, end = 40.dp)
) {
Text(text = address?.mailBox ?: "Country")
}
Spacer(modifier = Modifier.height(20.dp))
Text(
text = address?.fullAddress ?: "Full Address",
modifier = Modifier.padding(start = 40.dp, end = 40.dp)
)
}
}
What is the issue here! Any way to solve it?
Thanks For your answers in Advance!
Android Studio doesn't use your Application class to render previews. Therefore, dependency injection doesn't work.
The suggested workaround for this is, to create another composable which takes the parameters from the view model.
For example, if you have a screen called ShoppingCartScreen
in which you inject a view model, create another screen with the name ShoppingCartScreenContent
. ShoppingCartScreen
then extracts all the data from the view model and passes it as parameters into ShoppingCartScreenContent
.
Alternatively both composables can have the same name. They will just differ by their parameter types.
In your case, you can refactor it like this:
@Composable
fun MainScreen(){
val addressViewModel = getAddressViewModel()
val address by addressViewModel.addressApiResponse.observeAsState(initial = null)
val onClick = remember { addressViewModel.callAddressApi() }
MainScreen(address, onClick)
}
@Composable
fun MainScreen(address: Address, onClick: () -> Unit){
Column(
modifier = Modifier
.fillMaxWidth()
.height(100.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Button(
onClick = onClick,
modifier = Modifier
.fillMaxWidth()
.height(40.dp)
.padding(start = 40.dp, end = 40.dp)
) {
Text(text = address?.mailBox ?: "Country")
}
Spacer(modifier = Modifier.height(20.dp))
Text(
text = address?.fullAddress ?: "Full Address",
modifier = Modifier.padding(start = 40.dp, end = 40.dp)
)
}
}
@Preview
@Composable
fun MainScreenPreview(){
MainScreen(
address = Address(), // Create some dummy data here
onClick = {}
)
}