Home > Software design >  Android Studio Compose preview render problem
Android Studio Compose preview render problem

Time:05-25

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 Compouse, 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)
      }

CodePudding user response:

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

CodePudding user response:

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.

  • Related