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

不使用NavHost,如何在NavigableListDetailPaneScaffold中实现SavedStateHandle功能?

在NavigableListDetailPaneScaffold中优化DetailsScreenViewModel的初始化与参数传递

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 00:07:04