You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Compose Multiplatform中如何注入ViewModel?Koin通用模块暂不支持求替代

在Compose Multiplatform中给Composable注入ViewModel的可行方案

1. 手动封装跨平台ViewModel容器

既然Koin暂时不支持common模块的ViewModel注入,你可以自己搞一套跨平台的ViewModel存储逻辑:

  • 在common模块定义ViewModelStore接口,只留获取ViewModel的方法
  • Android端直接用Jetpack的ViewModelStore实现接口,iOS端用字典做内存缓存
  • 用CompositionLocal把ViewModelStore传到Composable里,再写个通用扩展函数方便调用

示例代码:

// common模块
interface ViewModelStore {
    fun <T : ViewModel> getViewModel(key: String, factory: () -> T): T
}

// 定义CompositionLocal供Composable获取
val LocalViewModelStore = compositionLocalOf<ViewModelStore> { error("ViewModelStore未初始化") }

// 通用Composable扩展,简化获取逻辑
@Composable
inline fun <reified T : ViewModel> viewModel(factory: () -> T): T {
    val store = LocalViewModelStore.current
    return store.getViewModel(T::class.simpleName!!, factory)
}

// Android模块实现
class AndroidViewModelStore(private val viewModelStore: androidx.lifecycle.ViewModelStore) : ViewModelStore {
    override fun <T : ViewModel> getViewModel(key: String, factory: () -> T): T {
        return ViewModelProvider(ViewModelStoreOwner { viewModelStore }, object : ViewModelProvider.Factory {
            @Suppress("UNCHECKED_CAST")
            override fun <T : androidx.lifecycle.ViewModel> create(modelClass: Class<T>): T {
                return factory() as T
            }
        })[key, T::class.java]
    }
}

// iOS模块实现
class IOSViewModelStore : ViewModelStore {
    private val cache = mutableMapOf<String, ViewModel>()
    override fun <T : ViewModel> getViewModel(key: String, factory: () -> T): T {
        return cache.getOrPut(key) { factory() } as T
    }
}

2. 用MVIKotlin框架

MVIKotlin是专为跨平台设计的MVI框架,自带的Store就相当于ViewModel,支持在common模块直接注入和使用:

  • 在common模块定义Store,里面写业务逻辑
  • 用Koin或者其他跨平台DI框架注入Store实例
  • 在Composable里直接获取,配合collectAsState监听状态变化

示例代码:

// common模块定义Store
class CounterStore : Store<CounterState, CounterIntent, CounterLabel>() {
    private val _state = MutableStateFlow(CounterState(0))
    override val state = _state.asStateFlow()

    override fun handleIntent(intent: CounterIntent) {
        when (intent) {
            is CounterIntent.Increment -> _state.value = _state.value.copy(count = _state.value.count + 1)
        }
    }
}

// Composable中使用
@Composable
fun CounterScreen() {
    val store: CounterStore = get() // 假设用Koin注入
    val state by store.state.collectAsState()
    
    Button(onClick = { store.accept(CounterIntent.Increment) }) {
        Text("当前计数: ${state.count}")
    }
}

3. Koin平台特定注入+通用层封装

如果不想换DI框架,可以在平台模块里用Koin注入ViewModel,再通过通用接口暴露给common模块的Composable:

  • 在common模块定义ViewModel的接口,只暴露需要的方法和状态
  • 各平台实现这个接口,Android端用Jetpack ViewModel,iOS端用普通类
  • 用expect/actual定义平台特定的获取方法,在Composable里直接调用

示例代码:

// common模块
interface CounterViewModel {
    val count: StateFlow<Int>
    fun increment()
}

// Android模块实现
class AndroidCounterViewModel : ViewModel(), CounterViewModel {
    private val _count = MutableStateFlow(0)
    override val count = _count.asStateFlow()
    override fun increment() { _count.value++ }
}

// Android模块的Koin注入
val androidModule = module {
    viewModel { AndroidCounterViewModel() }
}

// common模块的Composable
@Composable
fun CounterScreen(viewModel: CounterViewModel = getPlatformViewModel()) {
    val count by viewModel.count.collectAsState()
    
    Button(onClick = { viewModel.increment() }) {
        Text("计数: $count")
    }
}

// 平台特定的获取方法
expect fun getPlatformViewModel(): CounterViewModel

// Android端actual实现
actual fun getPlatformViewModel(): CounterViewModel {
    return getKoin().get<AndroidCounterViewModel>()
}

// iOS端actual实现(假设用Koin注入iOS版本的ViewModel)
actual fun getPlatformViewModel(): CounterViewModel {
    return getKoin().get<IOSCounterViewModel>()
}

内容的提问来源于stack exchange,提问作者Ahsan Ullah Rasel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 21:20:24