Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to create a ViewModel object inside composable function correctly?

I have this structure in my MainActivity:

val navController = rememberNavController()
NavHost(
    navController = navController,
    startDestination = ItemsScreen.route
) {
    composable(
        route = ItemsScreen.route
    ) {
        ItemsScreen(
            navController = navController
        )
    }
    composable(
        route = ItemDetailsScreen.route + "/{itemId}",
        arguments = mutableStateListOf(
            navArgument("itemId") {
                type = NavType.StringType
            }
        )
    ) { backStackEntry ->
        val itemId = backStackEntry.arguments?.getString("itemId") ?: ""
        ItemDetailsScreen(
            navController = navController,
            itemId = itemId
        )
    }
}

In the ItemDetailsScreen a LazyColumn:

LazyColumn {
    items(
        items = itemsResponse.data
    ) { item ->
        ItemCard(
            item = item,
            onItemClick = {
                navController.navigate(ItemDetailsScreen.route + "/${item.id}")
            }
        )
    }
}

And on item click I navigate to the ItemDetailsScreen:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

fun ItemDetailsScreen(
    navController: NavController,
    itemId: String,
    viewModel: ItemDetailsViewModel = hiltViewModel()
) {
    Log.d(TAG, itemId)
}

As seen, the ViewModel object is created in the constructor. When I open the ItemDetailsScreen the log statement is triggered twice. If I comment this line:

//viewModel: ItemDetailsViewModel = hiltViewModel()

The log statement works as expected, it prints the itemId only once. How to use the ViewModel object so it can trigger the log statement only once?

Here is also the ViewModel class:

@HiltViewModel
class ItemDetailsViewModel @Inject constructor(
    private val useCases: UseCases
): ViewModel() {
    private val _itemState = mutableStateOf<Response<Item>>(Success(Item()))
    val itemState: State<Response<Item>> = _itemState

    fun getItem(id: String) {
        viewModelScope.launch {
            useCases.getItem(id).collect { response ->
                _itemState.value = response
            }
        }
    }
}

>Solution :

You need not worry about the log statement being printed twice. There’s nothing wrong with the viewModel creation code. The log statment is printed twice because it is directly inside a composable function and composables recompose a lot (sometimes even in an unpredictable way). If you want to do some operation only when the composable is composed the first time, put it inside a LaunchedEffect block.
Check out the documentation to learn more about these effect handlers.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading