Home > Blockchain >  How to create a ViewModel object inside composable function correctly?
How to create a ViewModel object inside composable function correctly?

Time:12-06

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:

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
            }
        }
    }
}

CodePudding user response:

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.

  • Related