Android Studio Chipmunk 2021.2.1;
Compose Version = '1.1.1';
Gradle Version 7.4.2;
Kotlin 1.6.10;
Up to one point, everything was working. Then this error appeared and the preview stopped working when I try to call "LocalContext.current" and make "context.applicationContext as Application" both in this project and in another one. Where it used to work with "LocalContext.current"
Tried on different versions of Compose, kotlin, gradle.
java.lang.ClassCastException: class
com.android.layoutlib.bridge.android.BridgeContext cannot be cast to
class android.app.Application
(com.android.layoutlib.bridge.android.BridgeContext and
android.app.Application are in unnamed module of loader
com.intellij.ide.plugins.cl.PluginClassLoader @3a848149) at
com.client.personalfinance.screens.ComposableSingletons$AccountScreenKt$lambda-2$1.invoke(AccountScreen.kt:136)
at
com.client.personalfinance.screens.ComposableSingletons$AccountScreenKt$lambda-2$1.invoke(AccountScreen.kt:133)
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.material.MaterialTheme_androidKt.PlatformMaterialTheme(MaterialTheme.android.kt:23)
at
androidx.compose.material.MaterialThemeKt$MaterialTheme$1$1.invoke(MaterialTheme.kt:82)
at
androidx.compose.material.MaterialThemeKt$MaterialTheme$1$1.invoke(MaterialTheme.kt:81)
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.material.TextKt.ProvideTextStyle(Text.kt:265
@Preview(showBackground = true)
@Composable fun PrevAccountScreen() {
val context = LocalContext.current
val mViewModel: MainViewModel =
viewModel(factory = MainVeiwModelFactory(context.applicationContext as Application))
AccountScreen(navController = rememberNavController(), viewModel = mViewModel)
}
5
Answers
This is a wrong line
viewModel(factory = MainVeiwModelFactory(context.applicationContext as Application)
You can see your "as" is incorrect.
Try to create an empty ViewModel without context.
It solves the problem with a preview
I found the best way to get a Preview to work when you need to access something that’s Android lifecycle-specific, e.g. Application, Activity, FragmentManager, ViewModel, etc, is to create an implementation of that interface that does nothing.
An example using FragmentManager:
Preview function:
Now your Preview function will render.
You can do the same thing with ViewModel – just make your ViewModel extend an interface.
In your case, I would suggest doing the steps for ViewModel outlined above, and not messing around with LocalContext whatsoever in your preview.
You can’t render a preview composable if you try to inject a ViewModel.
There is plenty of way to avoid this problem. But for me, the easiest and clearest way to do this is simply to NOT RENDER a composable who have a viewModel.
To do that you can simply extract the content of your MyView composable to another composable called MyViewContent.
The MyView composable will declare all states that the MyViewContent will need (he will be statefull) and the MyViewContent composable will have those values as parameter (he will be stateless).
This way you ensure to respect the state hoisting pattern because you have the major part of your UI that will be stateless thanks to MyViewContent
As I understand
Let’s imagine that your screen is divided into 2 sections, which have 2 subsections, and so on.
First you do this
But it’s impossible to render preview. So you can do like this (Pass simple stable types as parameters "top-down") if you want render whole screen
And get a nightmare due to the need to pass a huge number of parameters, some of which are not needed by this composable, but are needed by the nested
So you can pass as parameters everything that can be created in the preview (not only primitive types), but organize it like this
As you can see, viewmodel is located only in statefull
MainScreen
, and you can renderMainContent
and other composable segmentsIf you need preview of the function, that uses context you need to use safe cast. Here’s how I do it when inject httpClient to Coil.