Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jetpack compose pass parameter to viewModel

How can we pass parameter to viewModel in Jetpack Compose?

This is my composable

    @Composable
    fun UsersList() {
      val myViewModel: MyViewModel = viewModel("db2name") // pass param like this
    }

This is viewModel

    class MyViewModel(private val dbname) : ViewModel() {
        private val users: MutableLiveData<List<User>> by lazy {
            MutableLiveData<List<User>>().also {
                loadUsers()
            }
        }
    
        fun getUsers(): LiveData<List<User>> {
            return users
        }
    
        private fun loadUsers() {
            // Do an asynchronous operation to fetch users.
        }
    }
like image 550
ArtixModernal Avatar asked Jun 15 '21 07:06

ArtixModernal


4 Answers

you need to create a factory to pass dynamic parameter to ViewModel like this:

class MyViewModelFactory(private val dbname: String) :
    ViewModelProvider.NewInstanceFactory() {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T = MyViewModel(dbname) as T
}

then use your factory like this in composable functions:

@Composable
fun UsersList() {
    val myViewModel: MyViewModel =
        viewModel(factory = MyViewModelFactory("db2name")) // pass param like this
}

and now you have access to dbname parameter in your ViewModel:

class MyViewModel(private val dbname) : ViewModel() {
    // ...rest of the viewModel logics here
}
like image 133
Secret Keeper Avatar answered Oct 28 '22 00:10

Secret Keeper


The other solutions work, but you have to create a factory for each ViewModel which seems overkill.

The more universal solution is like this:

inline fun <VM : ViewModel> viewModelFactory(crossinline f: () -> VM) =
    object : ViewModelProvider.Factory {
        override fun <T : ViewModel> create(aClass: Class<T>):T = f() as T
    }

And use it like this:

@Composable
fun MainScreen() {
    val viewModel: MyViewModel = viewModel(factory = viewModelFactory {
        MyViewModel("Test Name")
    })
}

For ViewModel like this:

class MyViewModel(
  val name: String
):ViewModel() {}
like image 31
Mike Avatar answered Oct 28 '22 02:10

Mike


Usually there is no common case where you need to do this. In android MVVM viewmodels get their data from repositories through dependency injection.

Here is the official documentation to the recommended android architecture: https://developer.android.com/jetpack/guide#recommended-app-arch

like image 42
Daniel Weidensdörfer Avatar answered Oct 28 '22 02:10

Daniel Weidensdörfer


If you use Hilt, you get this for free in SavedStateHandle for view model.

Pass the argument to the composable that calls the view model and retrieve it with the same name on view model from saved state handle.

Like this:

On NavHost:

NavHost(
(...)
    composable(
            route = [route string like this $[route]/{$[argument name]}],
            arguments = listOf(
                navArgument([argument name]) { type = NavType.[type: Int/String/Boolean/etc.] }
            )
        ) {
            [Your composable]()
        }
    )
)

On view model:

class ViewModel @Inject constructor(savedStateHandle: SavedStateHandle) {

    private val argument = checkNotNull(savedStateHandle.get<[type]>([argument name]))
}

Your argument will magically appear without having a view model factory.

like image 2
Pedro Sequeira Avatar answered Oct 28 '22 02:10

Pedro Sequeira