简体   繁体   中英

How to create separate ViewModels per list item when using Compose UI?

I'm working on a trading app. I need to list the user stocks and their value (profit or loss) among with the total value of the portfolio.

For the holdings list, in an MVP architecture I would create a presenter for each list item but for this app I decided to use MVVM (Compose, ViewModels and Hilt ). My first idea was to create a different ViewModel for each list item. I'm using hiltViewModel() in the composable method signature to create instances of my ViewModel, however this gives me always the same instance and this is not what I want. When using MVVM architecture, is what I'm trying to do the correct way or I should use a single ViewModel? Are you aware about any project I could have a look at? The image below is a super simplification of my actual screen, each cell is complex and that's why I wanted to use a different ViewModel for each cell. Any suggestion is very welcome.

在此处输入图像描述

Unfortunately, HiltViewModelFactory is not a KeyedFactory . So as of now it does not support same viewModel with multiple instances.

Tracking: https://github.com/google/dagger/issues/2328

Hilt doesn't support keyed view models. There's a feature request for keyed view models in Compose, but we had to wait until Hilt supports it.

Here's a hacky solution on how to bypass it for now.

You can create a plain view model, which can be used with keys, and pass injections to this view model through Hilt view model:

class SomeInjection @Inject constructor() {
    val someValue = 0
}

@HiltViewModel
class InjectionsProvider @Inject constructor(
    val someInjection: SomeInjection
): ViewModel() {

}

class SomeViewModel(private val injectionsProvider: InjectionsProvider) : ViewModel() {
    val injectedValue get() = injectionsProvider.someInjection.someValue
    var storedValue by mutableStateOf("")
        private set

    fun updateStoredValue(value: String) {
        storedValue = value
    }
}

@Composable
fun keyedViewModel(key: String) : SomeViewModel {
    val injectionsProvider = hiltViewModel<InjectionsProvider>()
    return viewModel(
        key = key,
        factory = object: ViewModelProvider.Factory {
            override fun <T : ViewModel?> create(modelClass: Class<T>): T {
                @Suppress("UNCHECKED_CAST")
                return SomeViewModel(injectionsProvider) as T
            }

        }
    )
}

@Composable
fun TestScreen(
) {
    LazyColumn {
        items(100) { i ->
            val viewModel = keyedViewModel("$i")
            Text(viewModel.injectedValue.toString())
            TextField(value = viewModel.storedValue, onValueChange = viewModel::updateStoredValue)
        }
    }
}

You have to use Dagger version 2.43 (or newer), it includes the feature/fix to support keys in Hilt ViewModels

https://github.com/google/dagger/releases/tag/dagger-2.43

From the release description:

Fixes #2328 and #3232 where getting multiple instances of @HiltViewModel with different keys would cause a crash.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM