Android Compose MVVM - How to reference a viewModel object in a Composable function that doesn't take arguments?
Asked Answered
W

1

2

How would the @Composable ContentFeed() function access the viewModel which was created in the Activity? Dependency injection? Or is that a wrong way of doing things here? The viewModel should always have only have one instance.

// MainActivity.kt
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val viewModel by viewModels<MainViewModel>()
        setContent {
        PracticeTheme {
            // A surface container using the 'background' color from the theme
            Surface(color = MaterialTheme.colors.background) {
                PulseApp(viewModel)
            }
        }
    }
}

// TabItem.kt
typealias ComposableFun = @Composable () -> Unit

sealed class TabItem(var icon: Int, var title: String, var content: ComposableFun) {
    object Feed : TabItem(R.drawable.ic_baseline_view_list_24, "Feed", { ContentFeed() })
}

// Content.kt
@Composable
fun ContentFeed() {
    // I need viewModel created in MainActivity.kt here
}
Wester answered 13/12, 2021 at 12:32 Comment(0)
S
5

In any composable you can use viewModel<YourClassHere>():

Returns an existing ViewModel or creates a new one in the given owner (usually, a fragment or an activity), defaulting to the owner provided by LocalViewModelStoreOwner.

The only exception in Compose for now, when it's not bind to activity/fragment, is when you're using Compose Navigation. In this case the store owner is bind to each route, see this and this answers on how you can share store owners between routes.

Check out more about view models Compose in documentation.

Shuman answered 14/12, 2021 at 1:43 Comment(4)
Oh wow, I can't believe I missed that. What I've been doing so far is initializing the ViewModel in the onCreate() method of an activity and passing it down to all functions as an argument. So I should have just been doing instead is val viewModel = viewModel<MyViewModel>() in all the Composable functions that require that specific viewModel correct?Wester
@Wester correcty, you don't need to initialize anything in the activity. View model will be created when you need itShuman
@Wester We recommend screen-level composables use ViewModel instances for providing access to business logic and being the source of truth for their UI state. You should not pass ViewModel instances down to other composables.. developer.android.com/jetpack/compose/…Inductee
@Inductee "You should not" - this is more like architecture advice, there's no technical problems in doing so, but yes, not passing VM is making your code cleaner. Also screen here is not related to activity or fragment, it can be Compose Navigation route view.Shuman

© 2022 - 2024 — McMap. All rights reserved.