https://kotlinlang.org logo
n

Nick

09/15/2023, 4:42 PM
hi all, I was led here by this thread about scoping viewmodels correctly to a composable (this thread gets at the same thing, and I think comment 18 starts to get at some of my issues). I’ve got a
LazyColumn
(or a regular
Column
, since I know that the number of items is limited to less than 10) that has a number of `Card`s in it, each of which has their own view model instance (of the same view model class,
CardViewModel
, below). I’m seeing an issue where data in one card seems to be duplicated in another. In trying to chase that down, I started logging in the
init()
and
onCleared()
and noticed that those are called when the `Card`s are scrolled on-/off-screen. My understanding is that the viewmodel should be retained for the life of the scope (which is a
NavBackStackEntry
in this case)--I wouldn’t expect to see
onCleared()
called while the screen is still in scope. Is the pattern in the threads above still considered best practices for this situation? If so, is there a clean way to manage `factory`s being passed around? Please tell me if I can provide any more information--and thanks so much for your help!
z

Zach Klippenstein (he/him) [MOD]

09/15/2023, 6:44 PM
Please keep long code snippets inside the thread to keep the main channel more readable.
👍 1
n

Nick

09/15/2023, 6:45 PM
Copy code
@Composable
private fun cardViewModel(serialNumber: String): CardViewModel {
    val factory = EntryPointAccessors.fromActivity(
        LocalContext.current as Activity,
        MainActivity.ViewModelFactoryProvider::class.java
    ).cardViewModelFactory()

    return viewModel(
        factory = CardViewModel.provideFactory(
            factory,
            serialNumber,
        ),
        key = serialNumber
    )
}

@Composable
fun CardComponent(
    serialNumber: String
) {
    val viewModel = cardViewModel(serialNumber = serialNumber)

    CardView(viewModel, serialNumber)
}
Copy code
class CardViewModel @AssistedInject constructor(
    @Assisted private val serialNumber: String,
    private val navigationController: NavigationController,
    @Assisted private val savedStateHandle: SavedStateHandle,
) : ViewModel() {

    @AssistedFactory
    interface Factory {
        fun create(
            serialNumber: String,
            savedStateHandle: SavedStateHandle,
        ): DeviceCardViewModel
    }

    @Suppress("UNCHECKED_CAST")
    companion object {
        fun provideFactory(
            assistedFactory: Factory,
            serialNumber: String,
        ): AbstractSavedStateViewModelFactory = object : AbstractSavedStateViewModelFactory() {
            override fun <T : ViewModel> create(
                key: String,
                modelClass: Class<T>,
                handle: SavedStateHandle
            ): T {
                return assistedFactory.create(serialNumber, handle) as T
            }
        }