不使用NavHost,如何在NavigableListDetailPaneScaffold中实现SavedStateHandle功能?
1. 解决参数获取问题:利用SavedStateHandle传递导航参数
步骤1:导航时携带参数
在列表项点击事件中,通过ThreePaneScaffoldNavigator的navigateTo方法传递item标识:
// 列表项点击回调 onClick = { item -> navigator.navigateTo( destination = ThreePaneScaffoldDestination.Detail, args = bundleOf("itemId" to item.id) ) }
步骤2:在ViewModel中注入SavedStateHandle提取参数
恢复SavedStateHandle的注入,直接从其中获取导航参数,并在init阶段自动执行数据获取逻辑:
@HiltViewModel class DetailsScreenViewModel @Inject constructor( private val repository: ItemRepository, private val savedStateHandle: SavedStateHandle ) : ViewModel() { private val itemId: Int? = savedStateHandle["itemId"] // 用stateIn自动转换为StateFlow,无需手动管理协程 val editDetailUiState: StateFlow<EditDetailUiState> = combine( repository.requestAllItemDetails(itemId), repository.requestAllUnits() ) { item, allUnits -> // 根据业务逻辑转换为对应UiState EditDetailUiState(item = item, units = allUnits) }.stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5000), initialValue = EditDetailUiState.Loading ) }
2. 解决ViewModel提前初始化问题:通过Key限定ViewModel实例
大屏双窗格模式下,详情面板可能预加载导致ViewModel提前创建。为hiltViewModel指定唯一Key,确保仅当有效itemId存在时才创建对应ViewModel,且不同item对应独立实例:
@Composable fun DetailsScreen(navigator: ThreePaneScaffoldNavigator) { // 从导航参数中提取itemId val itemId = navigator.currentDestination?.args?.getInt("itemId") // 仅当itemId不为空时创建ViewModel,用itemId作为Key区分不同实例 val viewModel = itemId?.let { hiltViewModel<DetailsScreenViewModel>(key = "DetailsVM_$it") } viewModel?.let { vm -> val uiState by vm.editDetailUiState.collectAsStateWithLifecycle() // 根据uiState渲染详情UI } }
这样未选中列表项时不会初始化ViewModel,切换选中项时会生成新的ViewModel实例,避免数据混淆。
可选优化:延迟数据获取至UI层触发
若需更精确控制数据加载时机,可将数据逻辑封装为ViewModel函数,配合LaunchedEffect在itemId变化时触发:
// ViewModel中 fun fetchData(itemId: Int) { val combinedFlow = combine( repository.requestAllItemDetails(itemId), repository.requestAllUnits() ) { item, allUnits -> EditDetailUiState(item = item, units = allUnits) } viewModelScope.launch { combinedFlow.collect { editDetailUiState = it } } } // UI层 @Composable fun DetailsScreen(itemId: Int?) { val viewModel = hiltViewModel<DetailsScreenViewModel>() LaunchedEffect(itemId) { itemId?.let { viewModel.fetchData(it) } } // 渲染UI }
内容的提问来源于stack exchange,提问作者Kinyo356
相关产品推荐
相关产品推荐

