'ViewModels creation is not supported in Preview

I followed the official guide to create viewModel instance and it works perfectly. However, when there is any viewModel in the @composable, Android Studio isn't able to render the preview and with the error code ViewModels creation is not supported in Preview. Anyone got any solution?

P.S. using compose version 1.0.0-alpha06



Solution 1:[1]

You could use an approach that looks like this which will show up in the recommended video bellow:

@Composable
fun TestView(
    action: MainActions,
    viewModel: OnboardViewModel = getViewModel()
) {
    TestUI(onClick = viewModel.clickMethod())
}

@Composable
fun TestUI(onClick: () -> Unit) {}

@Preview
@Composable
fun TestUIPreview() {
    MaterialTheme() {
        TestUI(onClick = {})
    }
}

There is a recommendation from google in this video at the selected time: https://youtu.be/0z_dwBGQQWQ?t=573

Solution 2:[2]

I had exactly the same problem. The solution was: Extend the ViewModel with an interface

ComposeView:

@Composable
fun MyScreen(myVm: IMyViewModel = MyViewModel()) {
    Text(text = myVm.getTextA())
}


@Preview()
@Composable
fun MyScreenPreview() {
    MyScreen(myVm = MyViewModelPreview())
}

ViewModel:

abstract class IMyViewModel : ViewModel(){
    abstract val dynamicValue: StateFlow<String>
    abstract fun getTextA() : String
}

class MyViewModel : IMyViewModel() {
    private val _dynamicValue: MutableStateFlow<String> = MutableStateFlow("")
    override val dynamicValue: StateFlow<String> = _dynamicValue

    init {
    }

    override fun getTextA(): String {
        return "Details: ${EntityDb.getAllEntities().lastOrNull()?.details}"
    }
}

class MyViewModelPreview(override val dynamicValue: StateFlow<String> =    MutableStateFlow("no data")) : IMyViewModel() {
    override fun getTextA(): String {
        return ""
    }
}

Solution 3:[3]

All your composables must be stateless and when it comes to ViewModel its a good practice to pass the viewModel down.

// Base viewModel contract
interface MainViewModel {
     fun getData(): Data
}

// Real viewModel to use in production
class RealViewModel : MainViewModel {
      override fun getData(): Data = getRealData()
}

// Fake viewModel to use in Previews and Tests 
object FakeViewModel : MainVeiwModel {
      override fun getData(): Data = getFakeData()
}

@Composable
fun Composable(viewModel: MainViewModel) {
    ShowData(viewModel.getData())
}

@Preview
@Composable
fun ComposablePreview() {
    Composable(FakeViewModel)
}

You can also have a complex ViewModel that has tons of dependencies that will be injected somewhere. Create your ViewModel instance in top level of your hierarchy as it make sense.

Solution 4:[4]

You could use interfaces and hilt.

interface IMyViewModel {

    fun getTextA() : String

}

@HiltViewModel
class MyViewModel() : ViewModel(), IMyViewModel {
    fun getTextA() : String {
        //do some cool stuff
    }
}

class MyViewModelPreview() : IMyViewModel {
    fun getTextA() : String {
        //do some mock stuff
    }
}

@Composable
fun MyScreen(myVm = hiltViewModel<MyViewModel>()) {
    Text(text = myVm.getTextA())
}


@Preview()
@Composable
fun MyScreenPreview() {
    MyScreen(myVm = MyViewModelPreview())
}

In this point MyViewModel is an implementation of IMyViewModel annotated with @HiltViewModel and hiltViewModel make all the required wired for you, In preview you could use any other simple mock implementation.

If you need to provide some dependency to your view model use injected constructor with dagger injection(already supported by hilt). Obviously this dependency should be paced on your actual viewmodel and your preview implementations need to be just a wrapper class with no other dependency since they function is just satisfy arguments

@HiltViewModel
class MyViewModel @Inject constructor(
    private val myDependencyRepositoryOne: MyDependencyRepositoryOne,
    private val myDependencyRepositoryTwo: MyDependencyRepositoryTwo)
: ViewModel(), IMyViewModel {
    fun getTextA() : String {
        //do some cool stuff
    }
}

Here is another useful resource related to viewmodel injection in compose

Solution 5:[5]

what I am using:

  @Preview(showBackground = true)
    @Composable
    fun DefaultPreview() {
         MyMVIApp1Theme {
           val myViewModel = remember { AppViewModel() }
           Greeting(viewModel = myViewModel,"Android")
         }
    }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Blindsurfer
Solution 3 Mohsents
Solution 4
Solution 5 user3439901