前言
在 Jetpack Compose 中,有时我们定义了一些全局的事件是在ViewModel中监听的,有时需要进行路由跳转。
但是ViewModel 本身不应该直接控制页面跳转(Navigation),因为这会违反 关注点分离(Separation of Concerns) 原则:
- ViewModel 负责业务逻辑和状态管理;
- UI(Composable) 负责展示和导航。
但你可以通过 “事件驱动” 的方式,让 ViewModel 发出导航事件,由 UI 层监听并执行跳转。
推荐做法:
使用
NavigationEvent或SharedFlow/StateFlow
方案一(推荐)
使用 MutableSharedFlow 发送一次性导航事件
ViewModel中发送事件
1 | class MyViewModel : ViewModel() { |
使用
SharedFlow是因为它适合一次性事件(如导航、Toast),不会被重复消费。
Composable中监听
1 |
|
注意:
LaunchedEffect保证只收集一次,避免重复订阅。
方案二
使用封装的 NavigationEvent 类(更安全)
为避免事件重复消费或内存泄漏,可封装一个 Event 包装类(类似 Android 的 SingleLiveEvent):
1 | open class Event<out T>(private val content: T) { |
然后在 ViewModel 中:
1 | private val _navigationEvent = MutableStateFlow<Event<String>?>(null) |
在 Composable 中:
1 | LaunchedEffect(vm.navigationEvent) { |
优点:
确保事件只被消费一次,适合复杂场景。
最佳实践总结
| 做法 | 是否推荐 | 说明 |
|---|---|---|
ViewModel 通过 SharedFlow 发出导航事件 |
✅ 强烈推荐 | 解耦、安全、符合单向数据流 |
使用 Event<T> 包装一次性事件 |
✅ 推荐 | 防止重复消费 |