Akram Hussain
Akram Hussain

Reputation: 480

Clean way of passing CompositionLocal value to viewmodel in Jetpack Compose

I have to load reviews based on storeURL and the SKUs of the products. For SKUs I make a network call and get the products in a MutableFlow. The storeURL is in a compositionLocal. Now, how do I pass this storeURL from composition local to the viewModel.

This is the function for loading reviews in viewModel

    fun loadReviews(products: List<Storefront.Product>?, storeUrl: String) {
        viewModelScope.launch(Dispatchers.IO) {
            when (val result = getReviews(products, storeUrl)) {
                is Resource.Loading -> Unit

                is Resource.Success -> {
                    val data = result.data
                    data.whatIfNotNull {
                        reviews.value = it
                    }
                }

                is Resource.Error -> Unit
            }
        }
    }

This is how I want to do it in the init block

    init {
        viewModelScope.launch {
            productsList.collectLatest { products ->
                loadReviews(products, [HOW TO GET STORE URL FROM COMPOSITION LOCAL HERE])
            }
        }
    }

Upvotes: 3

Views: 1592

Answers (1)

Zun
Zun

Reputation: 1700

There are multiple ways. For starters you can avoid launching the productsList inside the init and instead have a function that launches it which gets called from a LaunchedEffect.

@Composable fun ProductScreen(viewModel: ProductViewModel) {
    val compositionStuff = Whatever.current
    LaunchedEffect(Unit) { viewModel.loadProducts(compositionStuff) }
}

Alternatively you can include a SavedStateHandle as parameter in your ViewModel and when instantiating the ViewModel (using Hilt) make sure it's properly scoped so navigation parameters are passed to it.

composable("/products/{storeUrl}") {
  val vm = hiltViewModel<ProductViewModel>(it)
  ProductScreen(vm)
}

@HiltViewModel
class ProductViewModel @Inject constructor(
  savedStateHandle: SavedStateHandle,
) : ViewModel () {
    private val storeUrl: String = savedStateHandle["storeUrl"]!!
    init { loadProducts(products, storeUrl) }
}

Upvotes: 1

Related Questions