一、专门用于一次性事件(专用于事件,不是状态);
完整示例:点击按钮 → 发送事件 → 页面收到显示 Toast
完全符合你要的:Toast、提示、一次性操作
1. 先看 ViewModel(核心:SharedFlow 发送事件)
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.launch
class MyViewModel : ViewModel() {
// 1. 定义:只在内部发消息
private val _eventFlow = MutableSharedFlow<String>()
// 2. 暴露:给外部页面只读接收
val eventFlow: SharedFlow<String> = _eventFlow
// 3. 点击按钮触发的方法
fun onButtonClick() {
viewModelScope.launch {
// 发送一条 Toast 消息
_eventFlow.emit("操作成功!这是一条一次性 Toast")
}
}
}
2. 再看页面(Compose)接收事件,弹 Toast
import android.widget.Toast
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.lifecycle.viewmodel.composeviewModel
import kotlinx.coroutines.flow.collect
@Composable
fun EventScreen(
viewModel: MyViewModel = viewModel()
) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
// -----------------------
// 关键:监听一次性事件
// -----------------------
LaunchedEffect(Unit) {
viewModel.eventFlow.collect { message ->
// 收到事件 → 弹出 Toast(只会执行一次!)
Toast.makeText(
this@EventScreen.context,
message,
Toast.LENGTH_SHORT
).show()
}
}
// 点击按钮发送事件
Button(onClick = { viewModel.onButtonClick() }) {
Text("点我触发一次性 Toast")
}
}
}
3. 这个例子到底干了啥?(超直白解释)
- ViewModel 里用 SharedFlow 发事件
只发,不保存,页面退到后台再回来不会重复收到
- 页面用 collect 接收
收到就弹 Toast,执行一次就消失
- 完美符合:一次性操作
✅ 弹窗
✅ Toast
✅ 页面跳转
✅ 提示信息
4. 为什么必须用 SharedFlow?(重点)
如果用 StateFlow:
❌ 旋转屏幕会再次收到事件
❌ 回到页面会重复触发 Toast
SharedFlow:
✅ 事件发完就没
✅ 不会重复消费
✅ 真正一次性
5. 最简单记忆法
- 显示内容(文字 / 列表 / 状态) → StateFlow
- 弹出提示 / 点击事件 / 跳转 → SharedFlow
二、多订阅者共享(一对多)
核心比喻(先记住)
SharedFlow = 广播一个电台说话 → 所有收音机同时听到
场景说明
我们做一个:
- ViewModel 里发送一条消息
- 页面 A 收到
- 页面 B 收到
- 页面 C 也收到
真正的 一对多、多订阅者共享!
1. ViewModel(只有一个,发送消息)
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.launch
class SharedViewModel : ViewModel() {
// 共享数据流 —— 一对多核心
private val _sharedFlow = MutableSharedFlow<String>()
val sharedFlow: SharedFlow<String> = _sharedFlow
// 发送消息:所有订阅者都会收到!
fun sendMessage(msg: String) {
viewModelScope.launch {
_sharedFlow.emit(msg)
}
}
}
2. 主页面(发送消息 + 3 个订阅者同时接收)
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.composeViewModel
import kotlinx.coroutines.flow.collect
@Composable
fun SharedFlowDemoScreen(
viewModel: SharedViewModel = viewModel()
) {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
// -----------------------------
// 订阅者 1
// -----------------------------
var msg1 by remember { mutableStateOf("等待消息...") }
LaunchedEffect(Unit) {
viewModel.sharedFlow.collect { msg ->
msg1 = "订阅者1收到:$msg"
}
}
Text(msg1)
Spacer(Modifier.height(10.dp))
// -----------------------------
// 订阅者 2
// -----------------------------
var msg2 by remember { mutableStateOf("等待消息...") }
LaunchedEffect(Unit) {
viewModel.sharedFlow.collect { msg ->
msg2 = "订阅者2收到:$msg"
}
}
Text(msg2)
Spacer(Modifier.height(10.dp))
// -----------------------------
// 订阅者 3
// -----------------------------
var msg3 by remember { mutableStateOf("等待消息...") }
LaunchedEffect(Unit) {
viewModel.sharedFlow.collect { msg ->
msg3 = "订阅者3收到:$msg"
}
}
Text(msg3)
Spacer(Modifier.height(30.dp))
// -----------------------------
// 按钮:发送一条消息
// -----------------------------
Button(onClick = {
viewModel.sendMessage("大家好!我是共享消息")
}) {
Text("点我 → 发送广播消息")
}
}
}
运行效果(你一点击按钮就会看到)
订阅者1收到:大家好!我是共享消息
订阅者2收到:大家好!我是共享消息
订阅者3收到:大家好!我是共享消息
用最通俗的话讲清楚特性
1. 一对多是什么?
- 一个发送者
- 多个接收者
- 同时收到同一条消息
就像:老师在教室说话 → 全班同学都听到。
2. SharedFlow 为什么能一对多?
因为它是 热流 + 广播模式:
- 不管有没有人订阅,它都可以发送
- 发送一次,所有订阅者都能收到
- 不绑定任何一个页面
3. 普通 Flow 做不到!
普通 Flow 是 一对一:
- 你发给 A,B 收不到
- 发给 B,A 收不到
- 必须一个一个发
SharedFlow 是 一对多:
- 一次发送
- 全部收到
最经典使用场景(真实开发)
- 登录成功后,多个页面同步刷新
- 主题切换,所有页面同步变颜色
- 发一条通知,所有页面同时响应
- 全局事件总线(EventBus)
超精简总结
- SharedFlow = 广播
- 一个发,全部收
- 一对多、多订阅者共享
- 一次性事件、全局通知 首选!
如果你愿意,我还能给你做一个跨页面共享(A 页面发消息,B、C、D 页面同时收到)
注意:viewModel() 和 composeViewModel() 的区别
一句话总结:
现在的新版 Android 开发(Compose),你只需要记住:统一用 viewModel() 就够了!
composeViewModel() 是老版本的旧名字,现在已经合并成同一个函数了。