Compose中如何在页面间传递自定义模型并实现双向通信?
Compose页面间传递自定义模型与双向通信方案
1. 用共享ViewModel实现(最推荐)
ViewModel的生命周期独立于页面导航,很适合跨页面共享复杂数据,读写数据就能实现双向通信。
示例代码:
// 定义共享ViewModel,存储自定义模型 class SharedDataViewModel : ViewModel() { val customModel = MutableStateFlow<CustomModel?>(null) } // 发送页面 @Composable fun SenderScreen(navController: NavHostController, viewModel: SharedDataViewModel) { val testModel = CustomModel(id = 1, name = "初始数据") Button(onClick = { // 把数据存入ViewModel后跳转 viewModel.customModel.value = testModel navController.navigate("receiver") }) { Text("跳转到接收页") } } // 接收页面 @Composable fun ReceiverScreen(navController: NavHostController, viewModel: SharedDataViewModel) { val currentModel by viewModel.customModel.collectAsState() currentModel?.let { model -> Column { Text("收到的数据:${model.name}") Button(onClick = { // 修改ViewModel中的数据,实现双向同步 viewModel.customModel.value = model.copy(name = "已修改数据") navController.popBackStack() }) { Text("返回并更新数据") } } } } // 在NavHost中注入共享ViewModel val sharedViewModel = viewModel<SharedDataViewModel>() NavHost(navController = navController, startDestination = "sender") { composable("sender") { SenderScreen(navController, sharedViewModel) } composable("receiver") { ReceiverScreen(navController, sharedViewModel) } }
2. 让自定义模型实现Parcelable,结合Bundle传递
如果只是单向传递复杂数据,可以让模型实现Parcelable接口,这样就能通过导航参数传递,也能适配SafeArgs的逻辑。
步骤:
- 给自定义模型添加Parcelable实现:
@Parcelize data class CustomModel(val id: Int, val name: String) : Parcelable
- 定义带参数的路由:
NavHost(...) { composable( route = "receiver", arguments = listOf( navArgument("customModel") { type = ParcelableType(CustomModel::class.java) } ) ) { backStackEntry -> val receivedModel = backStackEntry.arguments?.getParcelable<CustomModel>("customModel") ReceiverScreen(receivedModel) } }
- 跳转时传递参数:
val model = CustomModel(id = 1, name = "测试数据") navController.navigate("receiver") { arguments = bundleOf("customModel" to model) }
注意:这种方式更适合单向传递,若需要双向通信,ViewModel的方案会更省心,毕竟返回时回传数据需要额外处理。
3. 用回调函数实现返回时的数据回传
如果只需要在从接收页返回时把修改后的数据带回去,可以给导航逻辑添加回调处理。
示例代码:
// 发送页面 @Composable fun SenderScreen(navController: NavHostController) { var updatedModel by remember { mutableStateOf<CustomModel?>(null) } Button(onClick = { navController.navigateWithResult("receiver") { resultBundle -> updatedModel = resultBundle.getParcelable("updatedModel") } }) { Text("跳转到接收页") } updatedModel?.let { Text("返回的修改后数据:${it.name}") } } // 接收页面 @Composable fun ReceiverScreen(navController: NavHostController) { val originalModel = CustomModel(id = 1, name = "初始数据") Button(onClick = { val resultBundle = bundleOf( "updatedModel" to originalModel.copy(name = "修改完成") ) navController.popBackStackWithResult(resultBundle) }) { Text("返回并传递修改后的数据") } } // 给NavController添加扩展函数处理结果回调 fun NavController.navigateWithResult(route: String, onResult: (Bundle) -> Unit) { val callback = NavResultCallback(onResult) currentBackStackEntry?.savedStateHandle?.set("navResultCallback", callback) navigate(route) } fun NavController.popBackStackWithResult(result: Bundle) { val callback = previousBackStackEntry?.savedStateHandle?.get<NavResultCallback>("navResultCallback") callback?.onResult?.invoke(result) popBackStack() } // 实现Parcelable的回调类 class NavResultCallback(val onResult: (Bundle) -> Unit) : Parcelable { constructor(parcel: Parcel) : this(parcel.readSerializable() as (Bundle) -> Unit) override fun writeToParcel(parcel: Parcel, flags: Int) { parcel.writeSerializable(onResult as java.io.Serializable) } override fun describeContents(): Int = 0 companion object CREATOR : Parcelable.Creator<NavResultCallback> { override fun createFromParcel(parcel: Parcel): NavResultCallback = NavResultCallback(parcel) override fun newArray(size: Int): Array<NavResultCallback?> = arrayOfNulls(size) } }
内容的提问来源于stack exchange,提问作者Duddie
相关产品推荐
相关产品推荐

