Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jetpack Compose Preview Render Problem when casting LocalContext.current

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.

Render problem

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)
 }
like image 687
Anton Kotov Avatar asked Jun 25 '26 02:06

Anton Kotov


2 Answers

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:

@Composable
@OptIn(ExperimentalAnimationApi::class)
fun MyFragmentView(
    fragmentManager: FragmentManager
) {
    Button(modifier = Modifier.align(Alignment.End),
           onClick = {         
              MyDialogFragment().show(fragmentManager, "MyDialogTag")
           }
    ) {

        Text(text = "Open Dialog")
    }

}

Preview function:

object PreviewFragmentManager: FragmentManager()

@Preview
@Composable
fun MyFragmentViewPreview() {
    MyFragmentView(
        fragmentManager = PreviewFragmentManager
    )
}

Now your Preview function will render.

You can do the same thing with ViewModel - just make your ViewModel extend an interface.

import androidx.compose.runtime.*
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.viewmodel.compose.viewModel
import kotlinx.coroutines.flow.StateFlow

interface MyViewModel {
   val state: StateFlow<SomeState>
   fun doSomething(input: String)
}

class MyViewModelImpl: MyViewModel, ViewModel() {
   // implement interface's required values/functions
}

object PreviewViewModel: MyViewModel() 

@Composable
fun MyView(viewModel: MyViewModel = viewModel<MyViewModelImpl>()) {
   // UI building goes here
}

@Composable
@Preview
fun MyViewPreview() {
    MyView(viewModel = PreviewViewModel)
}

In your case, I would suggest doing the steps for ViewModel outlined above, and not messing around with LocalContext whatsoever in your preview.

like image 87
Chantell Osejo Avatar answered Jun 26 '26 16:06

Chantell Osejo


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

like image 25
Alexander Avatar answered Jun 26 '26 16:06

Alexander



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!